静的解析がstaticを付けろと言う

プログラムのソースコードの静的解析というのがある。

コードを解析して、バグの原因になりそうな場所をいろいろ抽出してくれる。

打率は1割どころかもっと低い気がしますが。


これはバグだろうと多くの人に納得してもらえる例としては、

if(uintval<0){...}

と符号無しの変数が0未満であることを評価しているのだが、

この条件は常に不成立である。すなわちこの中のコードは実行されない。

その中に何か必要と思って書いている処理があっても実行されないので、

何かミスをしているということである。

変数の型を間違えているのか、条件を間違えているのかはわかりませんが。

そういうので再確認するべきポイントをいろいろ教えてくれるわけですね。


まぁそうはいっても馬鹿らしいものは多いのである。

その1つにファイル内でしか使われていない関数・グローバル変数にstaticが付いていないというのがある。

今までは「意図通り」とか書いてスルーすることは多かったが、

今回はその指摘に従っても良いかもなとファイル内に閉じるものにstaticに付けて回った。

そしたら次は1つの関数でしか使われていない変数があるという指摘があった。

まぁ関数内のstatic変数にするという方法はあるんですけどね。


C言語のstaticというのは2つの意味がある。それぞれ意味が異なる。

関数・グローバル変数に付ける場合はファイル内でしか利用できない関数・変数であることを示す。

どうしてファイル内に閉じる関数をstaticで宣言することがバグ対策として推奨されるのか?

例えばfoo.cでこのように宣言された関数があったとする。

void foofunc1(int x, int y){ ... }

かたやbar.cでこういうプロトタイプ宣言を書いたとする。

int foofunc1(char x, char y);

するとそれはそれで通ってしまい、名前が同じだとリンカで結合されてしまう。

当然、ここまでミスをしているとより強い指摘になるのだが、

潜在的なリスクを減らすにはプロトタイプ宣言を共通のヘッダファイルに書くべきであると。

ヘッダファイルのプロトタイプ宣言と実際の記述が間違えているとコンパイラが気づけるから。

あるいは関数名・変数名をミスタイプしていたようなケースならば、

それはヘッダファイルに書かれていないということで気づける。

一方でヘッダファイルに書かない場合はstaticとしてファイル内に閉じると明示するべきだと。


今まではどちらかというとヘッダファイルに列挙する方を選ぶことが多かったが、

内部に閉じるならstaticにするのも手と考えてこちらにした。

実は他のファイルでも必要でしたということで起こると困るが、

そうなる可能性はもう低いんじゃないかという読みである。

すると、次には1つの関数でしか使われていないという指摘が出てきたわけである。


記述ミスやバグが潜んでいる可能性はあるんでよね。

int fooval;
int foofunc2(int y){
  if(y > 0){

    fooval = y;
  }
  return fooval*200;
}

この書き方だとy≦0でfoovalが実行された場合は、すでにメモリ上にあるfoovalの値に200を掛けた値が返る。

前回の値を保存して計算することが目的ならこれでよいのだが、

実はy≦0の場合は、別の値を入れるべきところを書き忘れていたとすると気づけない。

もしfoovalが関数内の一時変数ならば、foovalが初期化されない場合があると指摘される。(なんならコンパイラが教えてくれる)


一時変数と間違えていませんかという指摘でもあるのだが、

一方で関数内のstatic変数として宣言することもできますねという指摘でもある。

int foofunc2(_Bool init){
  static int cnt;
  if(init){
    cnt = 0;
  }else{
    cnt++;
  }
  return cnt;
}

こういう書き方をするとcntという変数は次回関数実行時にも残る。

グローバル変数として宣言したのとほぼ同じ効果が得られる。

あくまでもローカル変数なので他の関数からは参照できないんですけどね。


ただ、これはデバッグに不都合だろうということで意図通りなら修正しなくてよいと考えた。

実際にはstaticなローカル変数もグローバル変数のように割り付けられているのだけど、

実際のところ、このタイプのstatic変数ってわかりにくい気がするんだよな。


と、この2つのstaticは効果が異なるわけですね。

修飾子なしのグローバル変数→static付きのグローバル変数→static付きのローカル変数

と同じようなものがだんだんスコープが狭くなっていくという話ではあるのだが。

このことについてWikibooksでは「文脈に応じて異なる意味と効果を持つ多目的な修飾子」と書かれている。

C言語/static (Wikibooks)

こういうのしばしばあるんですよね。

C++だとstaticメソッド・変数ってのもあるんですよね。

これはインスタンスではなくクラスに属するメソッド・変数っていうので意味が全く異なる。

これはさらに理解に苦しむ話である。


staticを付けたグローバル変数はファイル内でしか参照されないので、

ファイル内で代入だけして読み出されない変数についてコンパイラに指摘された。

もしstatic付きでなければ他の誰かがリードするかもしれないけど、

static付きなのでファイル内になければないですねということである。

これはデバッグ目的で代入しているものなのでその通りなのだが。

そしたら最適化で消されちゃうのか? と思ったが、見た感じはメモリに格納しているようだった。

staticを付けることで最適化のされ方も少し変わっているように見えた。

一体どういうところに差があるのかよくわからないけど。

コンパイラの警告は __attribute__((unused)) とか付ければ回避できる。