C: 错误初始化数组的奇怪行为
C: strange behavior of wrong initialization of array
#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 循环在这里有所不同?
在第三种情况下,编译器警告您存在越界访问。该标准不要求它抱怨,但它确实这样做了。
对于前两种情况,思考发生了什么是没有意义的。你说第一个程序运行 fine。它没有 - 它有一个 UB。
对于您关于 3 和 4 如何改变任何内容的问题,它可能取决于堆栈框架的布局方式。由于对齐差异,return 地址在一种情况下可能会被破坏,但在另一种情况下不会。您将不得不查看生成的程序集文件以查看实际出了什么问题。
这个 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:同样,这特定于所选的具有 O0 优化级别的编译器 gcc 6.2。在你的情况下,原因可能完全不同。
C 语言中没有提到 阻止您 编写访问越界内存的代码,标准只是明确提到任何这样做的尝试都会导致 undefined behavior.
任何诊断,如果提供由编译器自行决定,可能与提供的编译器选项相关联,标准未提及此要求。
例如,for some compiler, the last snippet compiles just fine(也收到运行时错误)。
注意 1: 在显示的片段中,语句 不是 初始化,它们是 作业.
注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;
}
#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 循环在这里有所不同?
在第三种情况下,编译器警告您存在越界访问。该标准不要求它抱怨,但它确实这样做了。
对于前两种情况,思考发生了什么是没有意义的。你说第一个程序运行 fine。它没有 - 它有一个 UB。
对于您关于 3 和 4 如何改变任何内容的问题,它可能取决于堆栈框架的布局方式。由于对齐差异,return 地址在一种情况下可能会被破坏,但在另一种情况下不会。您将不得不查看生成的程序集文件以查看实际出了什么问题。
这个 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:同样,这特定于所选的具有 O0 优化级别的编译器 gcc 6.2。在你的情况下,原因可能完全不同。
C 语言中没有提到 阻止您 编写访问越界内存的代码,标准只是明确提到任何这样做的尝试都会导致 undefined behavior.
任何诊断,如果提供由编译器自行决定,可能与提供的编译器选项相关联,标准未提及此要求。
例如,for some compiler, the last snippet compiles just fine(也收到运行时错误)。
注意 1: 在显示的片段中,语句 不是 初始化,它们是 作业.
注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;
}