在调查问题时,我偶然发现了代码,这可以归结为以下示例:
const unsigned long long int MAX=9223372036854775807ULL; //2^63-1
void double_it(double *d){
for(unsigned long long int i=0;i<MAX; i++){
d[i]=2*d[i];
}
}
由于一些错误,for循环比内存运行得更远,程序崩溃了。但这不是有趣的部分。
使用gcc(gcc -O2 -Wall -std=c99 -c
)编译时,此代码会出现以下警告:
警告:迭代2305843009213693951ull调用未定义的行为[-Waggressive-loop-optimizations]
导致我不明白。
stackoverflow上有一些类似的问题,例如:
但是那些问题是整数溢出,这里计数器i
似乎没有溢出。
在没有-O2
的情况下编译相同的代码不会导致这样的警告,所以我猜-Waggressive-loop-optimizations
是一个重要的部分。
实际上,我有两个问题:
-O2
没有警告?如果这个代码有问题,无论是否优化,我都会认为它是错误的。g ++的行为是相同的(在coliru在线查看)。
答案 0 :(得分:2)
但是那些问题是整数溢出,这里的计数器似乎没有溢出。
为什么你会这么想?
d[i]
与*(d + i)
相同。 d + i
明显溢出,因为double的大小超过2(不完全确定是否在标准中的任何地方拼写出来,但是你的架构是这样的,这是一个非常安全的假设)。为了完全正确,sizeof与此并不完全相关,但这就是代码在编译器内部转化为内容。
在C11§6.5.6中,我们可以阅读:
如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义
我们可以扭转那句话的逻辑。如果添加明显溢出,那么它必定是未定义的行为。
您没有收到警告的原因是编译器没有义务对所有未定义的行为发出警告。这是编译器的礼貌。通过优化,编译器可以花更多时间推理代码的作用,因此它有机会发现更多不良行为。如果没有优化,就不会浪费时间去做。