プログラミング言語 C の新機能 Part XXIX: 選択文と反復文のブロック化
(1999/11/08 [月])
今度の C 言語では、コンパウンドリテラル (Compound Literal) の導入に伴い、選択文(if 文など)、反復文(for 文など)の扱いが変更になりました。具体的にはそれらの文は { } で囲まれているとして扱われることになりました。どういう意味でしょう? 例を示します。
(1)のように if 文でコンパウンドリテラルを使用して、ポインタ変数にそれを代入しています。さて、(*) で指しているものはいったいなんでしょう。実は、今度の C 言語では (2) のように、赤字で示されたブロックが暗黙的におぎなわれます。その結果として、a も b も指しているものの保証が無いことが保証されます。これは、将来の言語の使用を改定した時意図しない結果を巻き起こさないように導入したそうです。
ただこれの導入により、既存のプログラムの意味が変わってしまう場合があります。(3) を見てください。
(3) では始めに a==0, b==1 として enum 宣言をしています。関数 g の中では、sizeof のオペランドの中で b==0, a== 1 として enum 宣言をしています。したがって従来の C 言語では、enum{b,a} のスコープはずっと持続しているので、関数 g は 0 を返すことになります。しかし今度の C 言語では、if 文全体がブロックとして覆われています。したがって、enum{b,a} は if 文が終わった後は見ることができません。したがって、グローバルな enum{a,b} が見え、結果として関数 g は 1 を返してしまいます。
//(1)コンパウンドリテラルを含んだ関数 int f(int par) { int *a, *b; if( *(b = (int[]){par,1}) == 0) a = (int[])(par,2,4,8,16,32); else a = (int[])(1,3,5,7,9); //(*)ここで a と b が指しているのものは何か? return *a; } //(2) C9X での実際の扱い int f(int par) { int *a, *b; { if( *(b = (int[]){par,1}) == 0) { a = (int[])(par,2,4,8,16,32); } else { a = (int[])(1,3,5,7,9); } } //答え:a も b も指しているのものの保証はない return *a; } |
(1)のように if 文でコンパウンドリテラルを使用して、ポインタ変数にそれを代入しています。さて、(*) で指しているものはいったいなんでしょう。実は、今度の C 言語では (2) のように、赤字で示されたブロックが暗黙的におぎなわれます。その結果として、a も b も指しているものの保証が無いことが保証されます。これは、将来の言語の使用を改定した時意図しない結果を巻き起こさないように導入したそうです。
ただこれの導入により、既存のプログラムの意味が変わってしまう場合があります。(3) を見てください。
//(3) この変更が起こす問題 enum {a, b}; int g(void) { if(sizeof(enum{b,a}) != sizeof(int)) return a; return b; //b の返す値は何か? } //C89 の場合:g() == 0 //C9X の場合:g() == 1 |
(3) では始めに a==0, b==1 として enum 宣言をしています。関数 g の中では、sizeof のオペランドの中で b==0, a== 1 として enum 宣言をしています。したがって従来の C 言語では、enum{b,a} のスコープはずっと持続しているので、関数 g は 0 を返すことになります。しかし今度の C 言語では、if 文全体がブロックとして覆われています。したがって、enum{b,a} は if 文が終わった後は見ることができません。したがって、グローバルな enum{a,b} が見え、結果として関数 g は 1 を返してしまいます。
by seclan