かなり前にフォーラムで興味深い質問に偶然出会ったので、その答えを知りたいです。
次の C 関数を考えてみましょう。
f1.c
#include <stdbool.h>
bool f1()
{
int var1 = 1000;
int var2 = 2000;
int var3 = var1 + var2;
return (var3 == 0) ? true : false;
}
これは常に を返す必要がありますfalse
。var3 == 3000
関数main
は次のようになります。
メイン.c
#include <stdio.h>
#include <stdbool.h>
int main()
{
printf( f1() == true ? "true\n" : "false\n");
if( f1() )
{
printf("executed\n");
}
return 0;
}
f1()
は常に を返すためfalse
、プログラムは画面にfalse を1 つだけ出力すると予想されます。しかし、コンパイルして実行すると、 executedも表示されます。
$ gcc main.c f1.c -o test
$ ./test
false
executed
それはなぜでしょうか? このコードには何らかの未定義の動作があるのでしょうか?
注: でコンパイルしましたgcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
。
ベストアンサー1
他の回答で指摘されているように、問題はgcc
コンパイラ オプションを設定せずに を使用することです。これを行うと、デフォルトで「gnu90」と呼ばれるものが使用されます。これは、1990 年に廃止された古い C90 標準の非標準実装です。
古い C90 標準では、C 言語に大きな欠陥がありました。関数を使用する前にプロトタイプを宣言しないと、デフォルトでint func ()
(ここで、( )
は「任意のパラメータを受け入れる」という意味) になります。これにより、関数 の呼び出し規約が変更されますが、実際の関数定義は変更されません。とfunc
のサイズが異なるため、関数が呼び出されると、コードは未定義の動作を呼び出します。bool
int
この危険で無意味な動作は、1999 年に C99 標準がリリースされたときに修正されました。暗黙的な関数宣言は禁止されました。
残念ながら、バージョン 5.xx までの GCC は、デフォルトで古い C 標準を使用しています。コードを標準 C 以外のコードとしてコンパイルする理由はおそらくありません。したがって、25 年以上前の非標準の GNU のゴミではなく、最新の C コードとしてコードをコンパイルするように GCC に明示的に指示する必要があります。
プログラムを常に次のようにコンパイルすることで問題を修正します。
gcc -std=c11 -pedantic-errors -Wall -Wextra
-std=c11
(現在の) C 標準 (非公式には C11 として知られています) に従ってコンパイルする試みを半ば強制的に実行するように指示します。-pedantic-errors
上記のことを心から実行し、C 標準に違反する誤ったコードを記述した場合にコンパイラ エラーを返すように指示します。-Wall
つまり、あれば便利な追加の警告をいくつか提供してくれるということです。-Wextra
つまり、他にあればよいかもしれない追加の警告をいくつか教えてください。