1. ホーム
  2. c++

[解決済み] 未定義、未指定、および実装で定義された動作

2022-03-17 08:36:34

質問

とは何ですか? 未定義の動作 (UB) in C and C++? についてはどうですか? 未定義の動作 実装定義 ビヘイビア? 両者の違いは何ですか?

どのように解決するのですか?

未定義の動作 は、他の言語から来たプログラマーが驚くような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++標準のセクション2.14.5パラグラフ11によれば、このプログラムは 未定義の動作 :

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

しかし、待ってください、私はこれを問題なくコンパイルして、出力を得ることができます。 yellow 文字列リテラルは読み取り専用メモリに保存されるため、最初の代入を試みるとコアダンプになります。これはまさに未定義の動作の問題です。基本的に、標準では未定義動作を呼び出したら何でもあり(鼻歌鬼でも)なのです。もし、あなたの言語のメンタルモデルに従った正しい動作があるとすれば、そのモデルは単に間違っているのであって、C++標準は唯一の投票権を持っています。

未定義の動作の他の例としては、境界を越えて配列にアクセスすることが挙げられます。 ヌルポインタの再参照 , ライフタイム終了後のオブジェクトへのアクセス または書き込み 巧妙と言われる表現 のように i++ + ++i .

C++標準の1.9項では、未定義動作の危険度の低い2つの兄弟についても言及しています。 無指定動作 実装で定義された動作 :

この国際規格の意味記述は,パラメタ化された非決定論的抽象機械を定義する。

抽象的な機械の特定の側面と操作は、この国際規格で次のように記述されています。 実装定義 (例) sizeof(int) ). これらは抽象的な機械のパラメータを構成する。各実装には、これらの点に関する特性や動作を説明する文書を含める必要があります。

抽象機械の他のある側面と操作は、この国際規格で次のように記述されています。 不特定多数 (例えば、関数への引数の評価順序など)。可能な場合、この国際規格は許容される動作のセットを定義する。これらは、抽象機械の非決定的な側面を定義する。

その他の特定の操作は、本国際規格で次のように記述されています。 未定義 (例えば、ヌルポインタをデリファレンスした場合の効果)。[ 備考 : この国際規格は、未定義の動作を含むプログラムの動作について、何らの要求も課さない。 - エンディングノート ]

具体的には、1.3.24項に記載されています。

許容される未定義の動作は、以下の通りです。 状況を完全に無視し、予測不可能な結果をもたらす 翻訳中やプログラム実行中に、環境に特徴的な文書化された方法で動作する(診断メッセージの発行の有無にかかわらず)、翻訳や実行を終了する(診断メッセージの発行あり)、などです。

未定義の動作に遭遇しないためには、どうしたらよいのでしょうか?基本的には C++の良書 自分の言っていることを理解している著者によるものです。インターネットのチュートリアルは避けましょう。ブルズチャイルドは避けてください。