それはすべて要約すると
実装の品質:警告が正確で有用であればあるほど、それは優れています。すべてのプログラムに対して「このプログラムは未定義動作を呼び出す場合と呼び出さない場合があります」と出力し、それをコンパイルするコンパイラは、かなり役に立たないが、標準に準拠している。ありがたいことに、このようなコンパイラを作成する人は誰もいません:-)。
判別の容易さ:コンパイラーは、未定義の動作、未指定の動作、または実装定義の動作を簡単に判別できない場合があります。5レベルの深さの呼び出しスタックがあり、const char *
引数がトップレベルからチェーンの最後の関数に渡され、最後の関数がそれを最初の引数として呼び出すprintf()
とします。const char *
コンパイラがそれをチェックしconst char *
て、それが正しいことを確認しますか?(最初の関数がその値にリテラル文字列を使用すると仮定します。)const char *
がファイルから読み取られる場合はどうでしょうか。ただし、ファイルには、出力される値の有効な形式指定子が常に含まれることがわかっていますか?
成功率:コンパイラーは、未定義、未指定などである場合とそうでない場合がある多くの構成を検出できる場合があります。しかし、「成功率」は非常に低いです。その場合、ユーザーは多くの「未定義の可能性がある」メッセージを見たくありません。偽の警告メッセージが多すぎると、実際の警告メッセージが非表示になったり、「低警告」設定でコンパイルするようにユーザーに促したりする可能性があります。それは悪いことです。
あなたの特定の例のために、gcc
「未定義かもしれない」についての警告を与えます。printf()
フォーマットの不一致についても警告します。
しかし、すべての未定義/未指定のケースに対して診断を発行するコンパイラーが必要な場合、それが機能するかどうかは明確ではありません。
あなたが次のものを持っているとしましょう:
#include <stdio.h>
void add_to(int *a, int *b)
{
*a = ++*b;
}
int main(void)
{
int i = 42;
add_to(&i, &i); /* bad */
printf("%d\n", i);
return 0;
}
コンパイラは行について警告する必要があります*a = ++*b;
か?
gfがコメントで述べているように、コンパイラは未定義の動作について変換ユニット間でチェックできません。古典的な例は、あるファイルで変数をポインターとして宣言し、別のファイルで配列として定義することです。comp.lang.cFAQ6.1を参照してください。