伝えるべき恐ろしい話はありますか?GCC マニュアルは最近、-fstrict-aliasing と共用体を介したポインターのキャストに関する警告を追加しました。
[...]アドレスを取得し、結果のポインターをキャストし、結果を逆参照すると、キャストがユニオン型を使用している場合でも、未定義の動作が発生します[強調が追加されました]。
union a_union {
int i;
double d;
};
int f() {
double d = 3.0;
return ((union a_union *)&d)->i;
}
この未定義の動作を説明する例はありますか?
この質問は、C99標準が何を言っているか、何を言っていないかに関するものではないことに注意してください。今日のgccやその他の既存のコンパイラの実際の機能についてです。
推測にすぎませんが、1 つの潜在的な問題はd
to 3.0 の設定にある可能性があります。d
直接読み取られることはなく、「ある程度互換性のある」ポインターを介して読み取られることもない一時変数であるため、コンパイラーはわざわざそれを設定しない場合があります。そして、f() はスタックからガベージを返します。
私の単純で素朴な試みは失敗します。例えば:
#include <stdio.h>
union a_union {
int i;
double d;
};
int f1(void) {
union a_union t;
t.d = 3333333.0;
return t.i; // gcc manual: 'type-punning is allowed, provided...' (C90 6.3.2.3)
}
int f2(void) {
double d = 3333333.0;
return ((union a_union *)&d)->i; // gcc manual: 'undefined behavior'
}
int main(void) {
printf("%d\n", f1());
printf("%d\n", f2());
return 0;
}
CYGWIN で問題なく動作します:
-2147483648
-2147483648
アセンブラを見ると、gccが完全に最適化t
して いることがわかりますf1()
。事前に計算された答えを単純に保存します。
movl $-2147483648, %eax
whilef2()
は 3333333.0 を浮動小数点スタックにプッシュし、戻り値を抽出します。
flds LC0 # LC0: 1246458708 (= 3333333.0) (--> 80 bits)
fstpl -8(%ebp) # save in d (64 bits)
movl -8(%ebp), %eax # return value (32 bits)
また、関数もインライン化されています (これが微妙な厳密なエイリアシングのバグの原因になっているようです) が、ここでは関係ありません。(そして、このアセンブラーはそれほど関連性はありませんが、裏付けとなる詳細を追加します。)
また、アドレスを取得することは明らかに間違っていることに注意してください (または、未定義の動作を説明しようとしている場合は正しいです)。たとえば、私たちが知っているように、これは間違っています。
extern void foo(int *, double *);
union a_union t;
t.d = 3.0;
foo(&t.i, &t.d); // undefined behavior
同様に、これが間違っていることはわかっています。
extern void foo(int *, double *);
double d = 3.0;
foo(&((union a_union *)&d)->i, &d); // undefined behavior
これに関する背景説明については、例を参照してください。
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1422.pdf
http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html
http:// davmac.wordpress.com/2010/02/26/c99-revisited/
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
( = Google でページを検索し、キャッシュされたページを表示)
厳密なエイリアシング規則とは何ですか?
C++ での C99 の厳密なエイリアシング規則 (GCC)
最初のリンク、7 か月前の ISO 会議の議事録草案では、ある参加者がセクション 4.16 で次のように述べています。
ルールが十分に明確であると考える人はいますか? それらを本当に解釈できる人は誰もいません。
その他の注意事項:私のテストは gcc 4.3.4 で、-O2 を使用しました。オプション -O2 および -O3 は、-fstrict-aliasing を意味します。GCC マニュアルの例では、sizeof(double) >= sizeof(int);を想定しています。それらが不平等であっても構いません。
また、cellperformace リンクで Mike Acton が指摘しているように、-Wstrict-aliasing=2
ではなく 、ここの例で=3
は生成されます。warning: dereferencing type-punned pointer might break strict-aliasing rules