#author("2016-12-12T17:57:41+09:00","default:tutimura","tutimura")
#author("2016-12-12T17:58:48+09:00","default:tutimura","tutimura")
[[Cygwinでデバッグ]]
#contents

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

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

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

 alias gcc='gcc -Wall'

Windows のエクスプローラでは、この名前のファイルを作ることができないので、Cygwin 上での操作方法を書いておきます。次のコマンドを実行するとよいでしょう。
 echo "alias gcc='gcc -Wall'" >> ~/.bash_profile


*** 警告される例 [#r17b36b6]
上記のオプションを付けておくと、次のような時に警告してくれます。

-
 if (i = 0) { .... }
↓
 hoge.c:6:2: warning: suggest parentheses around assignment used as truth value
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);
↓
 hoge:4:9: warning: uninitialized variable 'val'
初期化されてない変数を使っています。ただし、この警告を出すには、gcc に -Wall オプションに加えて、最適化を指示する -O(オー)オプションも同時に付けておく必要があります。

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

-
 int val;  scanf("%f", val);
↓
 hoge.c:6:1: warning: format '%f' expects type 'float *', but argument 2 has type 'int'
scanf() のフォーマット (%f) と、それで書き込まれる変数 (val) の型が一致しているか、チェックして矛盾があると報告してくれます。ここでは %d とすべきでしょう。更に val の前に & が抜けています。 


*** 変数の値を表示させる [#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 ( x == 0 ) {
     printf("x is zero.\n");
 }

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

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

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS