C言語の基礎

int get(int *p) {
    int v = *p;
    if (p == NULL)
        return -1;
    return v;
}

上の関数を gcc で最適化を有効にしてコンパイルすると、if (p == NULL) の判定が削除されることがある。その理由として正しいものはどれか。

  1. A先に *p を読んでおり、p が NULL ならそこで未定義動作なので、p は NULL でないと仮定してよいから
  2. BNULL との比較は、規格で常に偽になると決まっているから
  3. C最適化では、関数の引数を調べる if 文は呼び出し側が確かめるものとみなされ、すべて削除してよい決まりだから
答えと解説を見る

正解:A. 先に *p を読んでおり、p が NULL ならそこで未定義動作なので、p は NULL でないと仮定してよいから

p が NULL なら、先に実行する int v = *p; が NULL ポインタの参照になり、その時点で未定義動作です。コンパイラは未定義動作が起きないと仮定してよいので、*p を読めた以上 p は NULL ではないと判断し、p == NULL の判定と return -1 を取り除きます。x86-64 の gcc 13 で -O2 を付けると、関数は *p を読んで返すだけのコードになり、-O0 では判定が残ります。判定を確実に効かせるには、*p を読む前に p を確かめます。

  • A. 正しい。参照が判定より先にあるので、p は NULL でないと推論されます。
  • B. NULL との比較は、p が NULL なら真になります。この関数では、判定より前の *p が原因です。
  • C. 引数を調べる判定は、一般には削除されません。削除されるのは、結果が決まっていると推論できる場合だけです。
この分野を解く 分野ページへ

C言語の基礎の参考書を探す PR

Amazonの検索結果へのリンクです。Amazonのアソシエイトとして、半導体教室は適格販売により収入を得ています。

C言語の基礎のほかの問題

  1. C言語の規格で、int型の大きさについて定められていることとして、正しいものはどれか。
  2. 組込みのプログラムで、32ビットの周辺レジスタの値を入れる変数を、unsigned int ではなく stdint.h の uint32_t で宣言することが多い。主な理由として正しいものはどれか。
  3. 整数どうしの割り算を含む上のコードを実行したときの出力として、正しいものはどれか。
  4. 前置と後置の ++ を使った上のコードを実行したときの出力として、正しいものはどれか。
  5. if 文の条件に = を書いた上のコードを実行したときの出力として、正しいものはどれか。