gcc优化错误或违反c++规则

gcc optimization bug or C++ rule broken?

本文关键字:c++ 规则 优化 错误 gcc      更新时间:2023-10-16

测试代码:

#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 %dn", t.m[0], t.m[1]);
    return 0;
}

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

$ g++ -O0 testf.cpp 
$ ./a.out 
0 1
$ g++ -O2 testf.cpp 
$ ./a.out 
1 123

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

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

更新:我接受了带有std细节的答案,但最大的帮助是下面链接的评论:是"struct hack"吗?技术上未定义的行为?

您的程序有未定义行为。:

t.m[1] = 123;

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

也是如此:
t.set(1, 1);

因为它基本上结束了:

m[1] = 1;

你不能期望从一个具有未定义行为的程序中得到任何东西——尤其是不一致的行为。

下面是来自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数组重叠。您可能会覆盖编译器在那里编写的某种神奇的元数据(当然,更可能是多态类型)。