54

まず、明確にするために、無効なポインターの逆参照について話しているのではありません。

次の 2 つの例を考えてみましょう。

例 1

typedef struct { int *p; } T;

T a = { malloc(sizeof(int) };
free(a.p);  // a.p is now indeterminate?
T b = a;    // Access through a non-character type?

例 2

void foo(int *p) {}

int *p = malloc(sizeof(int));
free(p);   // p is now indeterminate?
foo(p);    // Access through a non-character type?

質問

上記の例のいずれかが未定義の動作を呼び出しますか?

環境

この質問は、この議論に応えて提起されます。たとえば、ポインタ引数が x86 セグメント レジスタを介して関数に渡され、ハードウェア例外が発生する可能性があるという提案がありました。

C99 標準から、次のことを学びます (強調は私のものです)。

[3.17] 不定値- 未指定の値またはトラップ表現のいずれか

その後:

[6.2.4 p2]ポインタが指すオブジェクトがその存続期間の終わりに達すると、ポインタの値は不確定になります。

その後:

[6.2.6.1 p5]特定のオブジェクト表現は、オブジェクト タイプの値を表す必要はありません。オブジェクトの格納された値がそのような表現を持ち、文字型を持たない左辺値式によって読み取られる場合、動作は undefinedです。そのような表現が、文字型を持たない左辺値式によってオブジェクトのすべてまたは一部を変更する副作用によって生成される場合、動作は未定義です。このような表現はトラップ表現と呼ばれます。

これらすべてをまとめると、「死んだ」オブジェクトへのポインタへのアクセスにはどのような制限があるのでしょうか?

補遺

上記で C99 標準を引用しましたが、C++ 標準のいずれかで動作が異なるかどうかを知りたいです。

4

3 に答える 3

15

私の解釈では、非文字型のみがトラップ表現を持つことができますが、どの型も不定値を持つことができ、不定値を持つオブジェクトに何らかの方法でアクセスすると、未定義の動作が呼び出されます。最も悪名高い例は、初期化されていないオブジェクトをランダム シードとして使用する OpenSSL の無効な使用である可能性があります。

したがって、あなたの質問に対する答えは次のようになります。

ところで、ポイント先のオブジェクトだけでなく、ポインタ自体freeがorの後で不確定であることの興味深い結果reallocは、このイディオムが未定義の動作を呼び出すことです。

void *tmp = realloc(ptr, newsize);
if (tmp != ptr) {
    /* ... */
}
于 2013-06-10T13:33:30.340 に答える
-1

C++ ディスカッション

簡単な答え: C++ では、クラス インスタンスにアクセスして "読み取る" ことはできません。非クラスオブジェクトのみを「読み取る」ことができます。これは、左辺値から右辺値への変換によって行われます。

詳細な回答:

typedef struct { int *p; } T;

T名前のないクラスを指定します。議論のために、このクラスに名前を付けましょうT:

struct T {
    int *p; 
};

コピー コンストラクターを宣言しなかったため、コンパイラーは暗黙的に宣言し、クラス定義は次のようになります。

struct T {
    int *p; 
    T (const T&);
};

したがって、次のようになります。

T a;
T b = a;    // Access through a non-character type?

はい、確かに; これはコピー コンストラクターによる初期化であるため、コピー コンストラクターの定義はコンパイラーによって生成されます。定義は

inline T::T (const T& rhs) 
    : p(rhs.p) {
}

したがって、バイトの束ではなく、ポインターとして値にアクセスしています。

ポインター値が無効な場合 (初期化も解放もされていない)、動作は定義されていません。

于 2013-06-16T04:23:42.470 に答える