3

私は働いていて、組合の利用を考えていました。設計が構造体/クラスを実際に要求したため、私はそれに反対することにしましたが、最終的に次の仮説的な質問につながります。

この不自然な例のような共用体があるとします。

typedef union {
    char* array_c;
    float* array_f;
    int* array_i;
} my_array;

. . . 次に、配列の 1 つを割り当てて、別の場所から削除してみます。

my_array arr;
arr.array_f = (float*)(malloc(10*sizeof(float)));
free(arr.array_i);

malloc の実装方法により、技術的には定義されていませんが、これは機能すると思います。また、int と float とは異なり、配列が同じサイズになる可能性は低いですが、array_c を割り当てるときにも機能すると思います。

このテストは、類似した new と delete で繰り返すことができます。これらもうまくいくと思います。

言語仕様はこれを行うことで私を嫌うだろうと推測していますが、うまくいくと思います. 「オブジェクトではなく配列であっても、void* にキャストされた new-ed ポインターを削除しないでください」ビジネスを思い出させます。

質問: 仕様はこれを行うことについて何と言っていますか? 簡単に確認しましたが、特にこのケースに対処するものは見つかりませんでした。とにかく、これは機能的な観点から見れば、どれほど賢明ではないでしょうか(私は、これが明確さの観点からひどいことだと認識しています)。

これは純粋に衒学的な目的のための好奇心の問題です。

4

3 に答える 3

2

あなたは正確に正しいです。それはルールを破ります:

値が共用体型のオブジェクトのメンバーに格納される場合、そのメンバーに対応しないが他のメンバーに対応するオブジェクト表現のバイトは未指定の値を取りますが、共用体オブジェクトの値はそれによって型にはなりません。トラップ表現。
    - ISO/IEC 規格 9899、セクション 6.2.6.1

ただし、実装が通常行われる方法では、「偶然に」適切に機能します。freeを受け取るのでvoid *、パラメータは に変換されてvoid *に渡されfreeます。すべてのポインターは同じアドレスに配置され、へのすべての変換はvoid *それらの値を変更しないため、渡される最終的な値はfree、正しいメンバーが渡された場合と同じになります。

理論的には、実装は最後にアクセスされた共用体のメンバーを追跡し、最後に書き込んだメンバーとは異なるメンバーを読み取ると、値を破損する (またはプログラムをクラッシュさせるなど) 可能性があります。しかし、私の知る限り、実際の実装ではそのようなことはありません。

于 2012-06-29T04:01:56.257 に答える
2

設定したメンバーとは異なるメンバーにアクセスしているため、これは未定義の動作です。文字通り何でもできます。

実際には、これは通常は機能しますが、信頼することはできません。コンパイラとツールチェーンは意図的に悪意を持っているわけではありませんが、最適化が未定義の動作と相互作用して完全に予期しない結果をもたらす場合がありました。もちろん、malloc の実装が異なるシステムを使用している場合は、おそらく失敗するでしょう。

于 2012-06-29T04:02:24.860 に答える
-1

malloc() の実装とは関係ありません。この例のユニオンは、同じメモリ位置を使用して、3 つの「異なる」ポインターのいずれかを格納します。ただし、ポインターが何を指していても、すべてのポインターは同じサイズです。これは、使用しているアーキテクチャのネイティブの整数サイズです。32 ビット システムでは 32 ビット、64 ビット システムでは 64 ビットなどです。これは、ポインタがメモリ内のアドレスであり、整数で表すことができるためです。

arr がアドレス 0x10000 にあるとします (ポインタへのポインタです)。malloc() が 0x666660 のメモリ位置を見つけたとします。この値に arr.array_f を割り当てます。これは、ポインタ 0x666660 を位置 0x10000 に格納することを意味します。次に、float を 0x666660 から 0x666688 に書き込みます。

ここで、arr.array_i にアクセスしようとします。共用体を使用しているため、arr.array_i のアドレスは、arr.array_f および arr.array_c のアドレスと同じです。したがって、アドレス 0x10000 から再度読み取りを行い、ポインター 0x666660 を読み取ります。これは、以前に返されたポインタ malloc と同じであるため、先に進んで解放できます。

とは言っても、整数をテキストとして解釈したり、浮動小数点数を整数として解釈したりしようとすると、明らかに破滅につながります。arr.array_i[0] == 1 の場合、arr.array_f[0] は == 1 ではなく、arr.array_c[0] は文字 '1' とは無関係です。この方法でメモリを「表示」してみることができます (ループと printf()) - しかし、何も達成できません。

于 2012-06-29T05:29:50.927 に答える