[[Cygwinでデバッグ]]
#contents

** 不可解な動作 [#se83500f]
コンパイルエラーも出ないし、実行させてもエラーは出ないけれども、思った通りの動きをしない場合に、どこから調べればよいのか、ヒントを書いてみます。

*** 警告レベルを上げる [#m8730d3e]
gcc コンパイラには、オプションによって動作を切り替える指示を出すことができます。以下のように、
 gcc -Wall hoge.c
と "-Wall"(''W''arning ''all'' の意味)のオプションを付けると、警告してくれる項目が増えます。例えば次のような時に警告してくれます。

-
 if (i = 0) { .... }
if の条件を書くべきところで、代入 (=) を行っています。比較するなら == です。
> このような間違いを避けるために 0 == i と左右の項を入れ替えて書く流儀もありますが、変数同士を比較する場合には無力ですし、何より「比較される主体が0」という奇妙な条件式になるので、お薦めできません。確かに、これを警告できないコンパイラは、昔は多くありましたが、もうそんな時代でもないでしょう。
> ちなみに Java ではこの間違いはコンパイルエラーになります。Perl でも警告レベルを上げれば検出できます。Pascal では = が比較で := が代入と、元々がわかりやすい文法になっています。つまり、素直に i == 0 と書いても大丈夫な場面が多くあります。C でも警告レベルを上げることを覚える方が有用でしょう。
>> Java のコーディングスタイルを提案している著名な文書でさえも、"if (i=0) ..." がコンパイル可能、などと大きな間違いを書いているのは、嘆かわしいことです。→http://www.alles.or.jp/~torutk/oojava/codingStandard/writingrobustjavacode_pidid93_c2.html#doc1_id635

-
 int val;  printf("%d\n", val);
初期化されてない変数を使っています。ただし、この警告を出すには、gcc に -Wall オプションに加えて、最適化を指示する -O(オー)オプションも同時に付けます。

-
 int val=5;  printf("%f\n", val);
printf() のフォーマット(%f)と、それで表示される変数 (val) の型が一致しているか、チェックして矛盾があると報告してくれます。ここでは %f→%d か、あるいは int→double のどちらかをすべきころでしょう。
> printf() 関数は可変長引数をとるため、原理的にはプロトタイプ宣言による型チェックはできませんが、gcc はフォーマット文字列を解析してくれるようです。このようなチェックをしてくれるコンパイラは少数派であると思います。

-
 int val;  scanf("%f", val);
scanf() のフォーマット (%f) と、それで書き込まれる変数 (val) の型が一致しているか、チェックして矛盾があると報告してくれます。ここでは %d とすべきでしょう。更に val の前に & が抜けています。 


この "-Wall" オプションは、完成する a.exe には影響しないので、常につけておくことをお勧めします。.bashrc に以下のように書き込んでおけば、常にこのオプションがついていることになります。

 alias gcc='gcc -Wall'

*** 変数の値を表示させる [#qd562d66]

 printf("a = %d\n", a);
 fprintf(stderr, "a = %d\n", a);

*** 余分なセミコロン [#wb33caf7]
if/while/for の後ろに ";" が付いていると、ほぼバグです。

 if ( x == 0 );  ←この ";" が余分
 {
     printf("x is zero.\n");
 }

行末のセミコロンは違和感がないため、if/while/for の後ろについていても見逃してしまいがちですし、機械的なチェックも難しいです。
> 「ブロックの中身が空っぽ」というのは、確かに if では不自然ですが、for や while では多用される例もありますので、ブロックが空というだけでコンパイラが警告してくれると迷惑です。もっと複合的な条件で警告することは可能かもしれませんが、今のところ、そういうコンパイラは見たことがありません。
行末のセミコロンは違和感がないため、if/while/for の後ろについていても見逃してしまいがちです。
> 「ブロックの中身が空っぽ」というのは、確かに if では不自然ですが、for や while では多用される例もありますので、ブロックが空というだけでコンパイラが警告してくれることは期待できないでしょう。

このバグを未然に防ぐためには、ブロックの書き方を変更してみるのもよいかもしれません。例えば以下のようなスタイルであれば、このバグは起こり得ません。

 if ( x == 0 ) {
     printf("x is zero.\n");
 }

この {} のスタイルを好む人は、行数を少なくできることに意義を見出していることが多いので、バグの防止に関しては副作用と言えるかもしれません。しかしながら(痛い目にあった人にとっては)効果は大きいので、検討してみる価値は十分にあるでしょう。

> なお、ブロックのスタイルについては、C 言語の教科書たる K&R では、首尾一貫していることを重要視していて、どれがよいというような評価はしていません。
> また、世間のプロジェクトの多くではコーディングスタイルが決められていて、その中にブロックの書き方も規定されていて、個人の趣味が通用しないことも大いに考えられます。
> さらに、エディタによっては自動フォーマットの機能を持つものがあって、それで整形してしまえばどのスタイルを選んでいても関係なくなってしまいます。


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS