#include <stdio.h>
#include <stdlib.h>
int main() {
int step;
double position[4];
position[0] = 1;
for (step = 1;step<=4;step++){
position[step] = 99;
}
return 0;
}
可以无错误地编译,生成的程序可以运行。
然而,
#include <stdio.h>
#include <stdlib.h>
int main() {
int step;
double position[3];
position[0] = 1;
for (step = 1;step<=3;step++){
position[step] = 99;
}
return 0;
}
也可以编译,但程序无法运行:错误是Abort trap: 6
。
在上面两种情况下,(错误地)初始化数组的大小比我在for循环中填充的大一个。但为什么4
和3
会在这里发挥作用?
现在,更有趣的是,
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
int main() {
int step;
double position[4];
position[0] = 1;
position[1] = 99;
position[2] = 99;
position[3] = 99;
position[4] = 99;
return 0;
}
甚至无法编译(错误为array index 4 is past the end of the array (which contains 4 elements
)。那么为什么for循环在这里有所作为呢?
答案 0 :(得分:1)
在第三种情况下,编译器会提示您超出访问范围。该标准不要求它投诉,但它会这样做。
对于前两个案例,没有必要考虑发生了什么。你说第一个程序运行罚款。它没有 - 它有一个UB。
关于3&amp; 4改变任何东西,它可能取决于堆栈框架的布局方式。由于对齐差异,返回地址可能在一种情况下被破坏而在另一种情况下不会被破坏。您将不得不查看生成的程序集文件以查看实际出现的问题。
此https://godbolt.org/g/gqz39q表示如果您将数组大小设置为3,则position
位于%rbp - 32
而step
位于%rbp - 4
。因此,如果您编写position[3]
,step
被覆盖(我不想考虑正在编写的内容)。
当您将position
的大小设为4时,会将step
放在%rbp - 4
,将position
放在%rbp - 48
。现在您写信至position[4]
即%rbp - 48 + 4 * 8 = %rbp - 16
。这将写到%rbp - 8
。因此%rbp - 4
(step
)不会更改。
长话短说,填充在案例1中保存了你但在案例2中没有。
PS:这也是特定于所选编译器gcc 6.2的O0优化级别。在你的情况下,原因可能完全不同。
答案 1 :(得分:1)
C语言中没有提及阻止你编写访问超出内存的代码,标准只是明确提到任何这样做的尝试都会导致undefined behavior。
任何诊断(如果提供)由编译器自行决定,可能与提供的编译器选项相关联,标准中没有提到这一点。
例如,for some compiler, the last snippet compiles just fine(并且也接收运行时错误)。
注1:在显示的摘录中,语句不初始化,它们是分配。 < / p>
注2:我稍微修改了一下代码,但无效的访问是相同的
#include <stdio.h>
int main(void) {
//int step; // remove unused variable warning
double position[4];
position[0] = 1;
position[1] = 99;
position[2] = 99;
position[3] = 99;
position[4] = 99;
(void) position; // suppress unused variable warning
return 0;
}