Joh*_*itb 27 c++ optimization gcc strict-aliasing compiler-optimization
我有一个案例,朋友将类型为"Base"的非基类对象强制转换为类类型对象"Derived",其中"Derived"是"Base"的派生类,只添加函数,但没有数据.在下面的代码中,我确实x向派生类添加了一个数据成员
struct A {
int a;
};
struct B : A {
// int x;
int x;
};
A a;
int g(B *b) {
a.a = 10;
b->a++;
return a.a;
}
Run Code Online (Sandbox Code Playgroud)
通过严格的别名分析,GCC(也是Clang)总是返回10,而不是11,因为b永远不会指向a明确定义的代码.但是,如果我删除B::x(因为实际上是在我朋友的代码的情况下),GCC的输出汇编代码并没有优化的回归访问a.a并重新加载从内存中值.因此我的朋友的代码g在GCC(按照他的意图)调用"有效",即使我认为它仍然有未定义的行为
g((B*)&a);
Run Code Online (Sandbox Code Playgroud)
因此,在基本相同的两种情况下,GCC优化了一种情况并且没有优化另一种情况.是因为b可以合法地指出a?或者是因为GCC只是想破坏现实世界的代码?
我测试了答案
如果删除B :: x,那么B符合9p7中对标准布局类的要求,并且访问变得非常明确,因为这两种类型是布局兼容的,9.2p17.
有两个布局兼容的枚举
enum A : int { X, Y };
enum B : int { Z };
A a;
int g(B *b) {
a = Y;
*b = Z;
return a;
}
Run Code Online (Sandbox Code Playgroud)
汇编器输出g的回报1,不0,即使A和B是布局兼容(7.2p8).
所以我的另一个问题是(引用一个答案):"两个具有完全相同布局的类可能被认为"几乎相同"并且它们被排除在优化之外." .有人可以为GCC或Clang提供此证明吗?
如果删除B::x,则B符合9p7中对标准布局类的要求,并且访问变得非常明确,因为这两种类型是布局兼容的,9.2p17和成员都具有相同的类型.
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),也没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类,
- 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类.
如果两个标准布局结构类型具有相同数量的非静态数据成员,则它们是布局兼容的,并且相应的非静态数据成员(按声明顺序)具有布局兼容类型.
未定义的行为包括它确实有效的情况,即使它不应该.
根据此联合的标准用法,允许访问标头或数据成员的类型和大小字段:
union Packet {
struct Header {
short type;
short size;
} header;
struct Data {
short type;
short size;
unsigned char data[MAX_DATA_SIZE];
} data;
}
Run Code Online (Sandbox Code Playgroud)
这严格限于工会,但许多编译器支持这种扩展,只要"不完整"类型将以未定义大小的数组结束.如果从子类中删除额外的静态非成员,它确实变得微不足道并且布局兼容,这允许别名?
struct A {
int a;
};
struct B {
int a;
//int x;
};
A a;
int g(B *b) {
a.a = 10;
b->a++;
return a.a;
}
Run Code Online (Sandbox Code Playgroud)
但仍然执行别名优化.在具有相同数量的非静态成员的情况下,假定最派生的类与基类相同.让我们颠倒顺序:
#include <vector>
#include <iostream>
struct A {
int a;
};
struct B : A {
int x;
};
B a;
int g(A *b) {
a.a = 10;
b->a++;
return a.a;
}
int main()
{
std::cout << g((A*)&a);
}
Run Code Online (Sandbox Code Playgroud)
这与预期一样返回11,因为B显然也是A,与原始尝试不同.让我们进一步发挥
struct A {
int a;
};
struct B : A {
int foo() { return a;}
};
Run Code Online (Sandbox Code Playgroud)
除非foo()是虚拟的,否则不会导致别名优化.将非静态或const成员添加到B将导致"10"答案,添加非平凡的构造函数或静态不会.
PS.在第二个例子中
enum A : int { X, Y };
enum B : int { Z };
Run Code Online (Sandbox Code Playgroud)
这两者之间的布局兼容性由C++ 14定义,它们与底层类型(但可转换)不兼容.有点像
enum A a = Y;
enum B b = (B*)a;
Run Code Online (Sandbox Code Playgroud)
可能会产生未定义的行为,就像您尝试使用任意32位值映射float一样.
| 归档时间: |
|
| 查看次数: |
1406 次 |
| 最近记录: |