3

外部ライブラリから次のタイプがあるとします。

union foreign_t {
    struct {
        enum enum_t an_enum;
        int an_int;
    } header;
    struct {
        double x, y;
    } point;
};

次のコード フラグメントが、さまざまなプラットフォームやさまざまなコンパイラで期待どおりに機能すると想定しても安全ですか?

struct pair_t {
    double x, y;
};

union foreign_t foreign;
struct pair_t *p_pair;

p_pair = (struct pair_t *) &foreign;
p_pair->x = 1234;
p_pair->y = 4321;

/* Expected result: (1234, 4321) or something like that */
printf("(%lf, %lf)", foreign.point.x, foreign.point.y);

編集:

厳密なエイリアシングの提案に従って、次のテストを行いました。

#include <stdint.h>
#include <stdio.h>

int main()
{
    uint16_t word = 0xabcd;
    uint8_t tmp;
    struct {
        uint8_t low;
        uint8_t high;
    } *byte = (void *) &word;

    tmp = byte->low;
    byte->low = byte->high;
    byte->high = tmp;

    printf("%x\n", word);

    return 0;
}

上記の一見問題のないコードは信頼できません。

$ gcc -O3 -fno-strict-aliasing -otest test.c
$ ./test
cdab
$ gcc -O3 -fstrict-aliasing -otest test.c
$ ./test
abcd

開発者に平和はありません...

4

8 に答える 8

6

あなたが書いたように、どのアーキテクチャのほぼすべてのコンパイラでも動作するはずです。ただし、技術的には厳密なエイリアシング規則に違反していると思います。無関係なポインター型の間でキャストしているため、過度に積極的なオプティマイザーは、特定のポインターが互いにエイリアスを作成しないと想定しているため、特定のメモリの読み取りと書き込みを並べ替える可能性があります。

残念ながら、 の定義を変更できないと仮定すると、このコードを厳密なエイリアシングに対して完全に防弾する方法はないと思いますforeign_t。内部構造体には名前がないため、コンパイラーがエイリアス可能であると想定するポインターを構築する方法はありません。ただし、実際には、コードに問題が発生することはないと思います。

于 2009-10-16T00:59:08.470 に答える
2

はい、それは完全に移植可能でなければなりません。foreign. header. _

(書き込みheaderと読み取りをpoint行って、すべてのプラットフォームで同じように動作することを期待することはできません。しかし、そうする必要はないので、問題ないはずです。)

于 2009-10-15T22:07:02.923 に答える
2

はい、これは完全に合理的です。ANSI C 標準では、ある「型」を共用体に書き込んで別の型を読み取り、信頼できるものを取得することを期待してはならないことを示しています。ここでは、ある方法で共用体に何かを書き込んでから、同じ方法でそれを読みたいとします。ユニオンの先頭にパディングがなく、適切なポインターの配置が保証されているので、私が理解しているように、これで問題ないはずです。

于 2009-10-15T22:07:30.740 に答える
1

書かれているように、はい、どの単一のプラットフォームでも期待どおりに機能します。

のより典型的な定義では、型識別子フィールドを含むforeignをラップしunionて、structその型の各値に対して実行時に共用体の有効なブランチが明示的に認識されるようにします。

foreign興味深いのは、a の値をプラットフォーム A からプラットフォーム B に伝達し、期待されるデータを再び取得したい場合です。標準では IEEE 浮動小数点数や 2 の補数の 2 進整数を実際には必要としないため、アラインメント、サイズ、およびバイト オーダーの違いが最小限に抑えられ、場合によっては数値表現の違いにさえ遭遇します。

実際には、それほど悪くはありませんが、バイナリ交換フォーマットが必要な場合は、プラットフォーム固有のテスト ケースやコンパイル時のアサーションのいずれかによって軽減される移植性の懸念事項になります。

あるいは、プラットフォーム固有のデータをマーシャリングするstructunion、ストレージと送信のために明確に定義されたオクテットのシーケンスにデータをマーシャリングすることが、確実な答えです。これは、たとえば MPEG 標準で採用されているアプローチです。

于 2009-10-16T00:39:54.047 に答える
1

はい、同一の構造体には同じサイズと配置要件があることが保証されています。

于 2009-10-15T22:30:17.287 に答える
1

はい、これで正常に動作します。ただし、特定のアクションが特定のコンパイラで機能する場合でも、に書き込むとすぐforeign.headerに、 の内容に関してすべての賭けがオフになります。foreign.point

于 2009-10-15T22:04:43.953 に答える
0

doubleint、およびが一貫性を維持していると仮定するとenum(誓うつもりはありませんdoubleが、IEEE 標準だと思います)、確実に動作するはずです。ただし、intシステム ワードによって異なり、特定のサイズについては、enumが知る限り信頼できません。

于 2009-10-15T22:12:04.250 に答える
-1

次のコード フラグメントが、さまざまなプラットフォームやさまざまなコンパイラで期待どおりに機能すると想定しても安全ですか?

単純な答えはノーです...

マシンとコンパイラごとに、構造体を正しく整列させるコマンドを見つける必要があります。これは、移植性に関する主要な問題の 1 つです。

于 2009-10-20T00:20:32.177 に答える