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 循环中填充的数组小一。但为什么 43 在这里有所不同?

现在,更有趣的是,

#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 - 32step 放在 %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;
}