struct Vec
{
double x, y, z;
};
Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = (&vec.x)[2]; // (&vec.x)[2] should be equal to vec.z
变量 res 的值应该等于3.但是当我打开优化时,编译器会错误地重新排序指令并且 res 包含一些垃圾。一些可能的重新排序示例:
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
res = (&vec.x)[2]; // correct value
vec.x = 1.0;
vec.y = 2.0;
res = (&vec.x)[2]; // incorrect value
vec.z = 3.0;
vec.x = 1.0;
res = (&vec.x)[2]; // incorrect value
vec.y = 2.0;
vec.z = 3.0;
这是编译器中的错误吗?或者不允许像这样访问struct data members?
修改
我刚刚意识到之前的代码确实有效,对不起。但这不起作用:
Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = (&vec.x)[i]; // (&vec.x)[i] should be equal to vec.z when i == 2
当编译时不知道变量 i 时,编译器会错误地重新排序指令。
答案 0 :(得分:20)
您正在调用未定义的行为。 (&vec.x)[2]
与(&vec.x) + 2
等价,标准(§[expr.add] / 4)有关于指针添加的说法(强调我的):
添加或减去具有整数类型的表达式时 从指针开始,结果具有指针操作数的类型。如果 指针操作数指向数组对象 84 的元素,以及 数组足够大,结果指向一个元素偏移量 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。 换句话说,如果表达式P指向一个的第i个元素 数组对象,表达式(P)+ N(等效地,N +(P))和(P)-N (其中N的值为n)分别指向i + n和i - 数组对象的第n个元素,只要它们存在即可。而且,如果 表达式P指向数组对象的最后一个元素,即 表达式(P)+1指向数组对象的最后一个元素, 如果表达式Q指向一个数组的最后一个元素 对象,表达式(Q)-1指向数组的最后一个元素 宾语。 如果指针操作数和结果都指向元素 相同的数组对象,或一个超过数组的最后一个元素 对象,评估不得产生溢出;否则, 行为未定义。
另见上文提到的注释84:
84)为此目的,不是数组元素的对象被认为属于单元素数组;见5.3.1。
由于vec.x
不是数组,因此它被认为是单元素数组的元素。因此vec.x
和vec.z
不是同一数组的元素,并且行为未定义。
答案 1 :(得分:2)
除了未定义的行为之外,请注意C ++不会强制将它们彼此相邻地分配。如果你想通过索引和名称引用它们,我建议你采取另一种方式:
struct Vec
{
double elems[3];
double& x;
double& y;
double& z;
Vec()
: x( elems[0] ), y( elems[1] ), z( elems[2] )
{
}
};
如果你不能为每个对象提供可能的(但不是必需的)额外空间,你可以将x,y,z声明为返回引用的函数。
答案 2 :(得分:0)
我最终找到了一个解决方案,它可以防止指令重新排序,并且不会增加Vec结构的大小:
struct Vec
{
union
{
struct
{
double x, y, z;
};
double data[3];
};
};
Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = vec.data[i];
感谢您的回答。