违反严格别名,即使没有任何转换?
Violating strict-aliasing, even without any casting?
我想我真的在问:是别名 "transitive"?如果编译器知道 A 可能是 B 的别名,而 B 可能是 C 的别名,那么它肯定应该记住 A 可能因此是 C 的别名。也许这个 "obvious" 传递逻辑不是必需的?
为清楚起见,举个例子。对我来说,最有趣的例子是严格别名问题:
// g++ -fstrict-aliasing -std=c++11 -O2
#include <iostream>
union
{
int i;
short s;
} u;
int * i = &u.i;
int main()
{
u.i = 1; // line 1
*i += 1; // line 2
short & s = u.s;
s += 100; // line 3
std::cout
<< " *i\t" << *i << std::endl // prints 2
<< "u.i\t" << u.i << std::endl // prints 101
;
return 0;
}
g++ 5.3.0,在 x86_64(但不是 clang 3.5.0)上给出了上述输出,其中 *i
和 u.i
给出了不同的数字。但他们应该给出完全相同的数字,因为 i
定义在 int * i = &u.i;
并且 i
不会改变。
我有一个理论:当'predicting' u.i
的值时,编译器会询问哪些行可能会影响u.i
的内容。这显然包括第 1 行。和第 2 行,因为 int*
可以作为联合的 int
成员的别名。还有第 3 行,因为任何可以影响一个联合成员 (u.s
) 的东西都会影响同一联合的另一个成员。但是在预测 *i
时,它没有意识到第 3 行会影响 *i
处的 int
左值。
这个理论看起来合理吗?
我觉得这个例子很有趣,因为我没有在其中进行任何转换。我设法通过进行任何转换来打破严格别名。
从联合的非活动成员读取在 C++ 中未定义。 (在 C99 和 C11 中是合法的)。
所以,总而言之,编译器不需要assume/remember任何东西。
标准语:
N4140 §9.5[class.union]/1
In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.
只允许从 C++ 中最后写入的联合成员读取。
仅允许在 'similar' 类型(有关详细信息,请参阅 this Q/A)和 char
/unsigned char
之间使用联合外部别名。只允许通过char
/unsigned char
别名其他类型,不允许通过其他类型别名char
/unsigned char
。如果允许后者,那么所有对象都必须被视为可能对任何其他对象进行别名处理,因为它们可能是 'transitively aliased' 就像您通过 char
/unsigned char
.[=21= 描述的那样]
但因为情况并非如此,编译器可以安全地假定只有 'similar' 类型的对象和 char
/unsigned char
互为别名。
我想我真的在问:是别名 "transitive"?如果编译器知道 A 可能是 B 的别名,而 B 可能是 C 的别名,那么它肯定应该记住 A 可能因此是 C 的别名。也许这个 "obvious" 传递逻辑不是必需的?
为清楚起见,举个例子。对我来说,最有趣的例子是严格别名问题:
// g++ -fstrict-aliasing -std=c++11 -O2
#include <iostream>
union
{
int i;
short s;
} u;
int * i = &u.i;
int main()
{
u.i = 1; // line 1
*i += 1; // line 2
short & s = u.s;
s += 100; // line 3
std::cout
<< " *i\t" << *i << std::endl // prints 2
<< "u.i\t" << u.i << std::endl // prints 101
;
return 0;
}
g++ 5.3.0,在 x86_64(但不是 clang 3.5.0)上给出了上述输出,其中 *i
和 u.i
给出了不同的数字。但他们应该给出完全相同的数字,因为 i
定义在 int * i = &u.i;
并且 i
不会改变。
我有一个理论:当'predicting' u.i
的值时,编译器会询问哪些行可能会影响u.i
的内容。这显然包括第 1 行。和第 2 行,因为 int*
可以作为联合的 int
成员的别名。还有第 3 行,因为任何可以影响一个联合成员 (u.s
) 的东西都会影响同一联合的另一个成员。但是在预测 *i
时,它没有意识到第 3 行会影响 *i
处的 int
左值。
这个理论看起来合理吗?
我觉得这个例子很有趣,因为我没有在其中进行任何转换。我设法通过进行任何转换来打破严格别名。
从联合的非活动成员读取在 C++ 中未定义。 (在 C99 和 C11 中是合法的)。
所以,总而言之,编译器不需要assume/remember任何东西。
标准语:
N4140 §9.5[class.union]/1In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.
只允许从 C++ 中最后写入的联合成员读取。
仅允许在 'similar' 类型(有关详细信息,请参阅 this Q/A)和 char
/unsigned char
之间使用联合外部别名。只允许通过char
/unsigned char
别名其他类型,不允许通过其他类型别名char
/unsigned char
。如果允许后者,那么所有对象都必须被视为可能对任何其他对象进行别名处理,因为它们可能是 'transitively aliased' 就像您通过 char
/unsigned char
.[=21= 描述的那样]
但因为情况并非如此,编译器可以安全地假定只有 'similar' 类型的对象和 char
/unsigned char
互为别名。