编译器指令重新排序

时间:2016-06-29 03:00:53

标签: c++ optimization

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 时,编译器会错误地重新排序指令。

3 个答案:

答案 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.xvec.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];

感谢您的回答。