C:错误初始化数组的奇怪行为

时间:2017-04-12 04:56:18

标签: c arrays compiler-errors compiler-warnings

#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循环在这里有所作为呢?

2 个答案:

答案 0 :(得分:1)

在第三种情况下,编译器会提示您超出访问范围。该标准不要求它投诉,但它会这样做。

对于前两个案例,没有必要考虑发生了什么。你说第一个程序运行罚款。它没有 - 它有一个UB。

关于3&amp; 4改变任何东西,它可能取决于堆栈框架的布局方式。由于对齐差异,返回地址可能在一种情况下被破坏而在另一种情况下不会被破坏。您将不得不查看生成的程序集文件以查看实际出现的问题。

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 - 4step)不会更改。

长话短说,填充在案例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;
}