关于C++循环体没有可观察行为被编译器优化而导致的BUG
在写题目283. 移动零 (opens new window)的时候遇到的,仅仅是一个cout就会导致一个if条件的变化。
# 问题描述
如果运行下面的代码,会得到“超出时间限制”。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
size_t p1=0;
while(true)
{
cout<<endl;
if( (p1==nums.size()) || (nums[p1]==0) )
{
cout<<"ABC"<<endl;
break;
}
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果运行下面的代码(仅仅是把cout << endl注释),所有样例会输出"ABC"并且break(比如样例4的输入是[2,1]也会break)。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
size_t p1=0;
while(true)
{
//cout<<endl;
if( (p1==nums.size()) || (nums[p1]==0) )
{
cout<<"ABC"<<endl;
break;
}
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
为什么只是加了一个cout就会导致结果不同?if条件应该是false。怀疑和编译器优化有关?
最神奇的是,在if里面把if的条件输出是0。
# 尝试的可能
- if后面多加了分号导致if自己变成了一条语句?
仔细检查了下并没有多加。
- 有内存越界等UB行为?
看了下感觉没有?如果nums.size()
是0,由于短目性会直接条件为真,也就不会访问到nums[p1]
这个位置。
# 原因(不完全确定)
根据知乎 (opens new window)、github有关llvm的issue (opens new window),基本可以确定是编译器优化导致的,可以认为是编译器的BUG。
根据C++ ISO标准规定,如果一个死循环内部没有“可观察行为”,则允许编译器将其删除从而进行优化。
假设这个循环被编译后这条指令的入口地址是A,里面有一个if,编译的时候if里面的内容编译后的入口地址报放到了循环的后面,假设是地址B,if成功会跳转到这个地址B执行。
巧合的是,B正好在A的下面,也就是B=A+1。
理论上的表现应该是,运行到A,进入循环体,if不成立,因为while从而跳回到A,这样往复死循环。
但是编译器把这个空循环优化了,导致pc跳过循环直接跑到了循环下面,也就是从A直接运行到了A+1。而此地址A+1正好是本来应该if跳转的位置B,也就是if里面的内容,所以表现就是运行了if里面的东西,实际上是因为运行的时候跳过了这个循环,直接跑到了这个本应该是if跳转过来的地址。
编辑 (opens new window)
上次更新: 2025/03/29, 14:50:20
- 01
- LLM聚合平台客户端对比03-29
- 03
- 使用腾讯云OSS作为个人网站文件处理的存储库02-18