14

これまでのところ、次のように推測する方法が見つかりません。

int* ptr;
*ptr = 0;

未定義の動作です。

まず、5.3.1/1 には、*に変換T*されるindirection を意味することが記載されていTます。しかし、これはUBについては何も言いません。

次に、3.7.3.2/4 がよく引用され、null 以外のポインターで割り当て解除関数を使用するとポインターが無効になり、その後の無効なポインターの使用は UB であると述べられています。しかし、上記のコードでは、割り当て解除については何もありません。

上記のコードで UB をどのように推定できますか?

4

7 に答える 7

13

セクション4.1は候補のように見えます(強調鉱山):

非関数、非配列型 T の左辺値 (3.10) は、右辺値に変換できます。T が不完全な型の場合、この変換を必要とするプログラムは形式が正しくありません。左辺値が参照するオブジェクトが T 型のオブジェクトではなく、T から派生した型のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムの動作は未定義です。T が非クラス型の場合、右辺値の型は T の cv 非修飾バージョンです。それ以外の場合、右辺値の型は T です。

仕様で「uninitial」を検索するだけで、より多くの候補が見つかるはずです。

于 2010-11-26T14:18:39.060 に答える
5

OPの質問はナンセンスです。標準が特定の動作が未定義であると言う必要はありません。実際、そのような表現はすべて、人々を混乱させ、標準を必要以上に冗長にするため、標準から削除することを主張します。

標準では、特定の動作が定義されています。問題は、この場合の動作を指定するかどうかです。そうでない場合、明示的にそう言っているかどうかにかかわらず、動作は未定義です。

実際、未定義のものがあるという仕様は、主に標準の作成者向けのデバッグ支援として標準に残されています。これは、ある場所に要件があり、別の場所の未定義の動作の明示的なステートメントと競合する場合に矛盾を生成するという考えです。 : これは、標準の欠陥を証明する方法です。未定義の動作の明示的なステートメントがなければ、動作を規定する他の節は規範的であり、挑戦されないでしょう.

于 2010-12-13T18:01:50.773 に答える
4

初期化されていないポインタを評価すると、未定義の動作が発生します。ポインターを逆参照するには最初にそれを評価する必要があるため、これは、逆参照も未定義の動作を引き起こすことを意味します。

これは C++11 と C++14 の両方に当てはまりますが、文言は変更されています。

C++14 では、[dcl.init]/12 で完全にカバーされています。

自動または動的ストレージ期間を持つオブジェクトのストレージが取得されると、そのオブジェクトの値は不確定になり、オブジェクトの初期化が実行されない場合、そのオブジェクトはその値が置き換えられるまで不確定な値を保持します。

評価によって不確定な値が生成された場合、次の場合を除き、動作は未定義です。

ここで、「次のケース」は に対する特定の操作unsigned charです。


C++11 では、[conv.lval/2] は、左辺値から右辺値への変換手順 (つまり、 で示されるストレージ領域からポインター値を取得する) の下でこれをカバーしましたptr

非関数型、非配列型 T の glvalue は、prvalue に変換できます。T が不完全な型の場合、この変換を必要とするプログラムは形式が正しくありません。glvalue が参照するオブジェクトが T 型のオブジェクトでも、T から派生した型のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムの動作は未定義です。

太字の部分は C++14 用に削除され、[dcl.init/12] の余分なテキストに置き換えられました。

于 2015-06-08T01:11:51.837 に答える
3

これについてよく知っているふりをするつもりはありませんが、一部のコンパイラはポインターを NULL に初期化し、ポインターを NULL に逆参照することは UB です。

また、初期化されていないポインターが何かを指す可能性があることを考慮すると (これには NULL が含まれます)、逆参照すると UB であると結論付けることができます。

セクション 8.3.2 [dcl.ref] の注記

[注: 特に、null 参照は明確に定義されたプログラムには存在できません。そのような参照を作成する唯一の方法は、null ポインターを逆参照することによって取得された「オブジェクト」にバインドすること であり、未定義の動作が発生するためです。9.6 で説明されているように、参照をビットフィールドに直接バインドすることはできません。]

— ISO/IEC 14882:1998(E)、ISO C++ 標準、セクション 8.3.2 [dcl.ref]

代わりにこれをコメントとして書くべきだったと思いますが、よくわかりません。

于 2010-11-26T14:13:52.423 に答える
3

ポインターを逆参照するには、ポインター変数から読み取る必要があります (それが指すオブジェクトについては話していません)。初期化されていない変数からの読み取りは未定義の動作です。

ポインターを読み取った後にポインターの値をどうするかは、この時点ではもう問題ではありません。(例のように) 書き込みを行うか、それが指すオブジェクトから読み取るかは問題ではありません。

于 2010-11-26T14:41:28.553 に答える
1

メモリ内の何かの通常のストレージにトラップ ビットまたはトラップ表現の「余地」がない場合でも、ユーザー コードが保持される可能性がある場合を除いて、静的期間変数と同じ方法で自動変数を格納する実装は必要ありません。どこかにそれらへのポインター。この動作は、整数型で最も顕著です。典型的な 32 ビット システムでは、次のコードが与えられます。

uint16_t foo(void);
uint16_t bar(void);
uint16_t blah(uint32_t q)
{
  uint16_t a;
  if (q & 1) a=foo();
  if (q & 2) a=bar();
  return a;
}
unsigned short test(void)
{
  return blah(65540);
}

testが65540 を生成することは特に驚くべきことではありませんが、その値は の表現可能な範囲外uint16_tであり、トラップ表現を持たない型です。型のローカル変数uint16_tが不定値を保持する場合、それを読み取ると の範囲内の値が得られるという要件はありませんuint16_t。このような方法で符号なし整数を使用すると予期しない動作が発生する可能性があるため、ポインターがさらに悪い方法で動作しないと期待する理由はありません。

于 2015-06-08T16:31:40.827 に答える