gcc优化错误或违反c++规则
gcc optimization bug or C++ rule broken?
测试代码:
#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
数组重叠。您可能会覆盖编译器在那里编写的某种神奇的元数据(当然,更可能是多态类型)。
- 此代码是否违反一个定义规则
- 生成文件不对文件使用隐式规则
- 变量可能尚未初始化[MIRA 2012规则9.1,强制性]
- 静态结构和一个定义规则
- 尽管遵循了规则,内存泄漏在哪里
- 这是关于成员访问规则的正确摘要吗
- uint_not_usable_without_attribute在业力规则中使用数字生成器时静态断言失败
- 增强精神解析器规则以检测语句中的特殊结尾
- 制作文件:没有规则来制定目标:如何设置正确的规则?
- 为什么此指针值不能转换为整数的规则是什么?
- 传递通用函数,用于梯形规则的数值积分
- C++内存模型中的确切规则阻止在获取操作之前重新排序
- 模板如何影响C++中隐式声明的规则?
- antlr 规则上下文是否可以独立于目标
- Bison/flex 在识别规则后等待输入
- 生成文件中隐式规则中的 -c 标志出错
- 单链接列表实现,规则为 3
- 指针算术规则中的"possibly-hypothetical"是什么意思?
- 假设声明中某些上下文中需要的名称查找规则是什么
- C++ 用于检查容器类中是否存在函数和隐式推导规则的概念