f()は、署名であるはずなのに返されません。これをコンパイルできるようにする理由はなぜですか?C標準がコンパイラに失敗させることを要求しない理由はありますか?
私はそれが未定義の振る舞いとすべてであることを知っています、しかしなぜそれはそもそも許可されているのですか?歴史的な理由はありますか?
double f(){}
int main()
{
f();
return 0;
}
C標準がコンパイラに失敗させることを要求しない理由はありますか?
未定義の動作を呼び出すことにより、C標準はコンパイラーの複雑さを軽減しました。実際、if
ステートメントなど、関数が値を返すかどうかを判断するのが難しい場合があります。
int f(int n)
{
if (n > 0) return 1;
}
f(5)
と、コンパイラは関数が正しいと簡単に言うことができます。 f(-5)
と、未定義の戻り値も簡単に検出できます。しかし、たとえば引数がユーザー入力からのものである場合、コンパイラーは関数が値を返すかどうかをどのように知ることができるでしょうか。これは有効なプログラムまたは間違ったプログラムの両方である可能性があるため、C標準では、コンパイラーが必要なことを実行できます。Cは、可能な限りスマートでシンプルになるように設計されています。
コンパイラーは、関数のすべてのコードパスを確実に分析し、関数が常に意味のある値を返すことを証明できない場合は、プログラムを拒否できます。この規格で義務付けられていない理由は、昔はコンパイラが現在のコンパイラよりもはるかに洗練されていなかったためだと思います。
もちろん、そのような関数の戻り値を使用することは未定義の動作です。
関数を終了する}に到達し、関数呼び出しの値が呼び出し元によって使用される場合、動作は未定義です。
関数が値を返さずに最後に到達することは簡単にわかります。それは戻りませんし、それが最後に到達するのを妨げる可能性のあるコードを呼び出しません(のようにabort()
)。
実際、欠落している戻り値は呼び出し元によって使用されないため、プログラムにはC99での未定義の動作はありません。6.9.1 / 12:
関数を終了する}に到達し、関数呼び出しの値が呼び出し元によって使用される場合、動作は定義されていません。
したがって、コードのスタイルは疑わしいものですが、明確に定義されています。
C ++標準は、ルールとその変更に関する注釈を変更し[diff.stat]
ます。int
ルールのCバージョンは、Cがreturnとreturnを区別していなかった時代に書き戻されたコードをサポートすることであると述べていvoid
ます。したがって、コードが最初に動作を定義した理由は「レガシー」です。double
とは言うものの、AFAIK Cは常にリターンとリターンを区別してきたので、適切なタイミングで実行されていれば、関数がリターンからフォールオフするint
ためにUBにすることができたはずです。double
戻り値が使用されているかどうかは別として、トリッカー関数を検討してください。
double f() {
if (g()) exit();
}
この関数にもreturnステートメントは含まれていませんが、実際にg
は常にtrue値を返すか、まったく返さない場合は、関数の最後に到達しません。したがって、この関数は、戻り値が使用されている場合でも受け入れられる必要があります。一般的なCの標準原則では、自分が何をしているかを理解し、自分の言うことを意味することが期待されます。が別のTUで定義されている場合g
は、コンパイラよりもそのことをよく知っているはずです。
従来の理由ではなかったとしても、コンパイラが検出する必要のある非リターンシナリオを定義するために、標準がテキストを追加することを気にすることはできないと確信しています。実装の品質に任されています。コンパイル時に関数がUBを回避できない可能性があると判断できる場合は、診断が不要であるにもかかわらず、コンパイラが警告を表示する可能性があります。さらに言えば、一般的なC実装者の原則に基づいて動作が定義されている場合、ユーザーが合理的に意味することができないほど巧妙なものがあることを警告することがあります。
コンパイラは、関数が実行時に戻るかどうかを判断できないためです。