L.C*_*.C. 16 c++ gcc strict-aliasing
我有一个问题,了解使用GCC的工会可以做什么和不可以做什么.我阅读了有关它的问题(特别是这里和这里),但他们关注C++标准,我觉得C++标准和实践(常用的编译器)之间存在不匹配.
特别是,我最近在阅读有关编译标志-fstrict-aliasing的GCC在线文档中发现了令人困惑的信息.它说:
-fstrict走样
允许编译器采用适用于正在编译的语言的最严格的别名规则.对于C(和C++),这将根据表达式的类型激活优化.特别地,假设一种类型的对象永远不会与不同类型的对象驻留在相同的地址,除非类型几乎相同.例如,a
unsigned int
can可以是aint
,但不是avoid*
或adouble
.字符类型可以别名为任何其他类型.特别注意这样的代码:Run Code Online (Sandbox Code Playgroud)union a_union { int i; double d; }; int f() { union a_union t; t.d = 3.0; return t.i; }
从不同的工会成员阅读的做法比最近写的那个(称为"打字式")很常见.即使使用-fstrict-aliasing,只要通过union类型访问内存,就允许类型为punning.因此,上面的代码按预期工作.
这是我认为我从这个例子和我的疑虑中理解的:
1)别名仅适用于相似类型或char
1)的后果:别名 - 正如文字暗示的那样 - 是你有一个值和两个成员来访问它(即相同的字节);
怀疑:当它们具有相同的字节大小时,两种类型是相似的吗?如果没有,什么是类似的类型?
1)对于非相似类型(无论这意味着什么)的后果,别名不起作用;
2)类型双关语是指我们读的不同于我们写的成员; 它是常见的,只要通过union类型访问内存,它就可以正常工作;
怀疑:在类型相似的特定情况下别名是什么类型?
我感到困惑,因为它表示unsigned int和double不相似,所以别名不起作用; 然后在示例中它是int和double之间的别名,它清楚地表明它按预期工作,但称之为类型 - 惩罚:不是因为类型是或不相似,而是因为它是从一个不写的成员读取.但是从一个没有写的成员那里读取的是我所理解的混淆(正如这个词所暗示的那样).我迷路了.
问题: 有人可以澄清别名和类型惩罚之间的区别,这两种技术的用途是如何在GCC中发挥作用的?编译器标志有什么作用?
别名可以从字面上理解它的含义:当两个不同的表达式引用同一个对象时.类型 - 双关语是"惩罚"一种类型,即将某种类型的对象用作不同类型.
形式上,类型惩罚是未定义的行为,只有少数例外.当你不小心弄乱位时,它通常会发生
int mantissa(float f)
{
return (int&)f & 0x7FFFFF; // Accessing a float as if it's an int
}
Run Code Online (Sandbox Code Playgroud)
例外是(简化)
char
,unsigned char
或,或std::byte
这称为严格别名规则:编译器可以安全地假设两个不同类型的表达式从不引用同一个对象(除了上面的例外),否则它们会有未定义的行为.这有利于优化,例如
void transform(float* dst, const int* src, int n)
{
for(int i = 0; i < n; i++)
dst[i] = src[i]; // Can be unrolled and use vector instructions
// If dst and src alias the results would be wrong
}
Run Code Online (Sandbox Code Playgroud)
gcc说的是它放松了一些规则,并允许通过工会打字,即使标准不要求它
union {
int64_t num;
struct {
int32_t hi, lo;
} parts;
} u = {42};
u.parts.hi = 420;
Run Code Online (Sandbox Code Playgroud)
这是类型双关语gcc保证将起作用.其他情况似乎可行,但有一天可能会被打破.
术语是个好东西,我可以随心所欲地使用它,其他人也可以!
当两种类型具有相同的字节大小时,它们是否相似?如果不是,类似的类型有哪些?
粗略地说,当类型的常量或符号不同时,它们是相似的。仅以字节为单位的大小绝对是不够的。
别名是类型相似的类型双关的特定情况吗?
类型双关是任何规避类型系统的技术。
别名是一种特殊情况,涉及将不同类型的对象放置在同一地址处。当类型相似时通常允许使用别名,否则禁止使用别名。此外,可以通过左值char
(或类似的char
)访问任何类型的对象,但执行相反的操作(即访问类型的对象char
)左值访问任何类型的对象,但不允许C 和 C++ 标准都保证了这一点,GCC 只是实现了标准的要求。
GCC 文档似乎在狭义上使用“类型双关语”,即读取除最后写入的联合成员之外的联合成员。即使类型不相似,C 标准也允许这种类型双关。OTOH C++ 标准不允许这样做。GCC 可能会也可能不会将权限扩展到 C++,文档对此并不清楚。
如果没有-fstrict-aliasing
,GCC 显然会放松这些要求,但尚不清楚具体放松到什么程度。请注意,这-fstrict-aliasing
是执行优化构建时的默认设置。
最重要的是,只需按照标准进行编程即可。如果 GCC 放宽了标准的要求,那么影响并不大,也不值得这么麻烦。
归档时间: |
|
查看次数: |
1374 次 |
最近记录: |