未定义的行为怪癖:在缓冲区外读取导致循环永远不会终止
Undefined Behavior quirk: reading outside a buffer causes a loop to never terminate?
我写了一个非常琐碎的程序,试图检查附加到缓冲区溢出的未定义行为。具体来说,关于在分配的空间之外对数据执行读取时会发生什么。
#include <iostream>
#include<iomanip>
int main() {
int values[10];
for (int i = 0; i < 10; i++) {
values[i] = i;
}
std::cout << values << " ";
std::cout << std::endl;
for (int i = 0; i < 11; i++) {
//UB occurs here when values[i] is executed with i == 10
std::cout << std::setw(2) << i << "(" << (values + i) << "): " << values[i] << std::endl;
}
system("pause");
return 0;
}
当我在Visual Studio上运行这个程序时,结果并不令人惊讶:读取索引10
会产生垃圾:
000000000025FD70
0(000000000025FD70): 0
1(000000000025FD74): 1
2(000000000025FD78): 2
3(000000000025FD7C): 3
4(000000000025FD80): 4
5(000000000025FD84): 5
6(000000000025FD88): 6
7(000000000025FD8C): 7
8(000000000025FD90): 8
9(000000000025FD94): 9
10(000000000025FD98): -1966502944
Press any key to continue . . .
但当我把这个程序输入Ideone.com的在线编译器时,我得到了非常奇怪的行为:
0xff8cac48
0(0xff8cac48): 0
1(0xff8cac4c): 1
2(0xff8cac50): 2
3(0xff8cac54): 3
4(0xff8cac58): 4
5(0xff8cac5c): 5
6(0xff8cac60): 6
7(0xff8cac64): 7
8(0xff8cac68): 8
9(0xff8cac6c): 9
10(0xff8cac70): 1
11(0xff8cac74): -7557836
12(0xff8cac78): -7557984
13(0xff8cac7c): 1435443200
14(0xff8cac80): 0
15(0xff8cac84): 0
16(0xff8cac88): 0
17(0xff8cac8c): 1434052387
18(0xff8cac90): 134515248
19(0xff8cac94): 0
20(0xff8cac98): 0
21(0xff8cac9c): 1434052387
22(0xff8caca0): 1
23(0xff8caca4): -7557836
24(0xff8caca8): -7557828
25(0xff8cacac): 1432254426
26(0xff8cacb0): 1
27(0xff8cacb4): -7557836
28(0xff8cacb8): -7557932
29(0xff8cacbc): 134520132
30(0xff8cacc0): 134513420
31(0xff8cacc4): 1435443200
32(0xff8cacc8): 0
33(0xff8caccc): 0
34(0xff8cacd0): 0
35(0xff8cacd4): 346972086
36(0xff8cacd8): -29697309
37(0xff8cacdc): 0
38(0xff8cace0): 0
39(0xff8cace4): 0
40(0xff8cace8): 1
41(0xff8cacec): 134514984
42(0xff8cacf0): 0
43(0xff8cacf4): 1432277024
44(0xff8cacf8): 1434052153
45(0xff8cacfc): 1432326144
46(0xff8cad00): 1
47(0xff8cad04): 134514984
...
//The heck?! This just ends with a Runtime Error after like 200 lines.
很明显,在他们的编译器中,用一个索引溢出缓冲区会导致程序进入无限循环!
现在,重申一下:我意识到我在这里处理的是未定义的行为。尽管如此,我还是想知道幕后到底发生了什么。物理上执行缓冲区溢出的代码仍然执行4个字节的读取,并将读取的内容写入(可能保护得更好)缓冲区。编译器/CPU是做什么导致这些问题的?
有两条执行路径导致正在评估的条件i < 11
。
第一个是在初始循环迭代之前。由于i
在检查之前已经初始化为0
,所以这是非常正确的。
第二个是在成功的循环迭代之后。由于循环迭代导致values[i]
被访问,而values
只有10个元素,因此这只能在i < 10
的情况下有效。如果i < 10
,在i++
之后,i < 11
也必须为真。
这就是Ideone的编译器(GCC)正在检测到的。条件i < 11
不可能为假,除非你有一个无效的程序,因此它可以被优化掉。同时,编译器不会特意检查你是否有一个无效的程序,除非你提供额外的选项来告诉它这样做(比如GCC/clang中的-fsanitize=undefined
)。
这是实现必须做出的权衡。他们可以支持无效程序的可理解行为,也可以支持有效程序的原始速度。或者两者兼而有之。GCC肯定非常关注后者,至少在默认情况下是这样。
相关文章:
- 如何读取单个字符并在输入两个字符序列时输出? 使用 while 循环和C++
- 逐字读取文本文件中的每一行并转换为 int(无限循环或崩溃?
- 虽然循环不读取整个 ifstream 文件
- 如何使用循环逐个读取C++中的多个文件
- 是否可以在文本文件中找到最长单词的长度,并在同一个文本文件中读取,只需 1 个 while 循环?
- For 循环在尝试读取数组 c++ 时程序崩溃
- 从文件 C++ 读取时的无限循环
- 从文件读取,并循环访问文件以写入数组
- C++:从文件中读取x y数据会产生无限循环吗
- 如何在不循环的情况下使用getline读取文件
- 如何在c++中读取do-while循环中的空白
- 在控制台上输出我从文件中读取的整数,将程序插入无限循环
- C++ while 循环没有从我的文件中读取 -1
- Boost.Process - 从具有休眠循环的进程读取
- 在嵌套循环中读取文件
- 无需使用循环,就将ISBN-10的前9位数字读取为字符串并计算第10位数字
- 如何在读取 c++ 输入时脱离循环
- C 使用getline()内部循环以在CSV文件中读取
- C 从文件功能读取循环时无法退出
- 我应该使用什么条件来停止文件读取循环