gcc优化bug还是C++规则坏了?

que*_*en3 0 c++ optimization gcc g++

这是测试代码:

#include <stdio.h>

struct test
{
   int m[1];
};

struct test2: public test
{
   int m1[22];
   void set(int x, int y) { m[x] = y; }
};

int main()
{
    test2 t;
    t.m[1] = 123;
    t.set(0, 0);
    t.set(1, 1);
    printf("%d %d\n", t.m[0], t.m[1]);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我没有编译一次,优化编译一次:

$ g++ -O0 testf.cpp 
$ ./a.out 
0 1
$ g++ -O2 testf.cpp 
$ ./a.out 
1 123
Run Code Online (Sandbox Code Playgroud)

在我看来,gcc看到数组大小m [1]并优化对它的访问,始终是第一个元素m [0].问题是:它是优化错误还是,某些C++规则被打破,以便gcc可以做它做的事情,如果是,那么什么规则?

请注意,由于额外的m1 [22]内存(在真实应用程序中设计),不会发生内存/堆栈溢出.我不问这是不是一个好的编程风格,我只是好奇得到上面问题的正确答案.

更新:我接受了std详细信息的答案,但最大的帮助是带有以下链接的注释:"struct hack"在技术上是不确定的行为吗?

And*_*owl 12

您的程序有未定义的行为.这里:

t.m[1] = 123;
Run Code Online (Sandbox Code Playgroud)

您正在写入一个越界位置(m是一个元素的数组,并且您正在索引一个不存在的第二个元素),对于以下情况也是如此:

t.set(1, 1);
Run Code Online (Sandbox Code Playgroud)

因为它基本上最终做了:

m[1] = 1;
Run Code Online (Sandbox Code Playgroud)

对于具有未定义行为的程序,您不能指望任何事情 - 尤其是不一致的行为.

  • @ queen3:如果`m`有一个元素,你就不能访问`m [1]`.数组索引是从零开始的,所以应该是`m [0]`. (2认同)

Ben*_*igt 10

这是5.7的相关规则:

当一个具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型...如果指针操作数和结果都指向同一个数组对象的元素,或者指向一个数组对象的元素数组对象的元素,评估不得产生溢出; 否则,行为未定义.

回想一下,t.m[i]它等同于*(t.m+i)指针添加规则.

显然,指针操作数t.m和结果(t.m + 1)不指向同一数组对象的成员.但是,在这种情况下,结果是"一个超过数组对象的最后一个元素".因此指针有效,但不能在严格的指针安全规则下解除引用.由于您试图取消引用它,因此您将回到未定义的行为.

请注意t.m + 1 == t.m1,由于允许编译器在基础子对象和成员之间插入填充,因此无法保证.

另请注意,编译器需要生成对表达式的正确位置的内存访问reinterpret_cast<int*>(reinterpret_cast<intptr_t>(t.m) + i * sizeof (int)),除非它定义__STDCPP_STRICT_POINTER_SAFETY__.但是没有具体说明它将如何与m1数组重叠.而且你可能会覆盖编译器在那里写的某种神奇的元数据(当然更可能是多态类型).