わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Cプログラミングのはまりどころ

Cプログラミングの「トラブルのもと」を,整理しました.

1. 比較に=

if (a = 3)

aに3が代入されます.そして条件式は(3ということで)常に真になります.
Cでの比較は,==です.

2. 1/2は0.5

double half = 1 / 2;

halfの初期値は,0.5ではなく0です.「整数割る整数は,整数」です.
上の文であれば,素直に0.5と書くべきですが,整数型変数による割り算を,double型にしたければ,(double)n / dのようにキャストを入れます.

3. &と&&,|と||を混同する

if (x >= 0 & x < 10 & a[x] != 0)

xがマイナスまたは10以上の場合でも,a[x]の参照をします.
2箇所ある「&」は,「&&」にしないといけません.論理演算子&&は,まず左オペランドを評価し,偽なら,右オペランドを評価することなく,全体を偽とします.&では,そうしてくれません.
PerlRubyなどに親しんでから,Cのコードを書くことになった人には,もう一つアドバイスを.「a ||= b;」とすると,aの値は「変わらないかb」ではなく,「0か1のいずれか」になります.

4. 初期化や代入をせずに,値を参照する

char *p;
p[0] = 'A';

これでは'A'が,どこに格納されるかわかりません.実行時エラーで終了するかもしれませんし,一応動くけれども,後々,何らかのバグの原因となるかもしれません.
ポインタ変数だけを宣言して,アドレス設定をせず,参照先に代入するのは,やめましょう.明示的な参照・代入だけでなく,strcpyやsprintfなどの第1引数にするのも,その内部動作を考えると,してはいけないのです.
ポインタに限りません.算術型でも,期待する値になってくれないときは,初期化や代入をせずに値を参照しているところがないか,変数ごとにチェックすべきです.

5. structを書き忘れる

struct point {
  double x, y;
};
point p;

もちろんコンパイルエラーです.「struct point p;」と宣言しないといけません.
「typedef struct {...} point;」と型定義をしたのなら,「point p;」とできます.

6. returnを書き忘れる

int factorial(int n)
{
  if (n <= 1) {
    1;
  } else {
    n * factorial(n - 1);
  }
}

これはコンパイル時に警告が出ます.関数処理を終え値を返したいときは,「return 値;」を習慣づけましょう.

7. ファイルを保存していないのにコンパイルする

テキストエディタのどこかに,編集中を表すサインが出ています.デフォルト設定のEmacsなら,ステータスバー左側(ウィンドウ左下)に「**」とあれば,編集中,すなわち,中身を変えたけれどそれを保存していないことを意味します.より具体的に言うと,テキストエディタというプロセスの中では,画面に見えている内容だけれども,ファイルの内容は,そうではないよという意味です.
とはいえ,コンパイルの対象は画面ではなくファイルです.コンパイルしてエラーが出て,テキストエディタをじっと見ても,原因は浮かび上がってこないわけです.
関連して,「テキストエディタで開いたファイルと,コンパイル対象のファイルが違う」ということも,ないようにしたいものです.