未定義、未指定、実装定義の動作 質問する

未定義、未指定、実装定義の動作 質問する

C および C++ における未定義動作(UB) とは何ですか?未指定動作実装定義動作についてはどうですか? それらの違いは何ですか?

ベストアンサー1

未定義の動作は、他の言語から来たプログラマーにとって驚くような C および C++ 言語の側面の 1 つです (他の言語では、未定義の動作をうまく隠そうとします)。基本的に、多くの C++ コンパイラーがプログラム内のエラーを報告しないとしても、予測どおりに動作しない C++ プログラムを作成することは可能です。

典型的な例を見てみましょう。

#include <iostream>
    
int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

変数はp文字列リテラルを指しており"hello!\n"、以下の2つの代入は文字列リテラルを変更しようとしています。このプログラムは何をするのでしょうか?C++ 標準、[lex.string] 注 4未定義の動作が発生します。

文字列リテラルを変更しようとした場合の影響は未定義です。

「でも待ってください、これは問題なくコンパイルして出力を得ることができますyellow」とか「undefined とはどういう意味ですか。文字列リテラルは読み取り専用メモリに格納されるので、最初の割り当て試行でコア ダンプが発生します」という声が聞こえてきそうです。これはまさに、undefined 動作の問題です。基本的に、undefined 動作を呼び出すと、標準では何でも許されます (鼻声の悪魔でさえ)。言語のメンタル モデルに従って「正しい」動作がある場合、そのモデルは単に間違っています。C++ 標準には唯一の投票権があります。

未定義の動作の他の例としては、

[イントロ定義]また、未定義の動作の危険性の低い 2 つの兄弟である、未指定の動作実装定義の動作も定義します。

実装定義の動作    [定義済み実装]

適切に構成されたプログラム構造と正しいデータのための動作は実装に依存し、各実装はそれを文書化する

未指定の動作    [定義: 未指定]

適切に構成されたプログラム構造と正しいデータの動作は実装に依存する

[: 実装では、どのような動作が発生するかを文書化する必要はありません。可能な動作の範囲は通常、このドキュメントで定義されます。—注記終了]

未定義の動作    [定義なし]

この文書で要件が課されていない動作

[注記: このドキュメントで動作の明示的な定義が省略されている場合、またはプログラムが誤った構造や誤ったデータを使用している場合、未定義の動作が予測されることがあります。 許容される未定義の動作は、状況を完全に無視して予測できない結果をもたらすものから、翻訳中またはプログラム実行中に環境の特性として文書化された方法で動作すること (診断メッセージの発行の有無にかかわらず)、翻訳または実行を終了すること (診断メッセージの発行あり) までの範囲にわたります。 [...] —注記終了]

未定義の動作を回避するにはどうすればいいでしょうか?基本的には、優れた C++ の本自分が何を話しているのかを知っている著者によるもの。インターネットのチュートリアルは避けてください。でたらめは避けてください。

おすすめ記事