1

我遇到了这两个代码块:

#include <stdio.h>
int main() {
  int a[10], i;
  for (i = 1; i <= 10; i++) {
    scanf("%d", &a[i]);
    printf("%d\n", a[i]);
  }
  return 0
}

当我运行第一段代码时,代码运行良好,但有时会被覆盖,我没有得到预期的结果。

但是,当我运行第二段代码时,程序运行得非常好,没有错误。

#include <stdio.h>
int main() {
  int size;
  scanf("%d", &size);
  int a[size], i;
  for (i = 1; i <= size; i++) {
    scanf("%d", &a[i]);
    printf("%d\n", a[i]);
  }
  return 0
}

为什么程序在第二种情况下运行完美?即使在第二种情况下,下标也会超过声明的数组大小。

4

3 回答 3

3

两个代码片段都有数组越界访问,因此您会看到未定义的行为

for(i=1;i<=size;i++)

如果您有大小数组,10则有效索引是0-9

一旦我们说未定义的行为,那么任何事情都可能发生,即使是崩溃。当您说它正在工作时,您可能没有看到崩溃,但您仍然在访问一些尚未分配的内存

于 2015-01-03T17:31:22.283 回答
1

问题中的代码(两种变体)表现出“未定义的行为”,因此“一切皆有可能”。这包括“程序崩溃”、“程序表现得好像溢出是合法的”和“程序尽最大努力清除机器上的所有数据”。幸运的是,编译器编写者很少使用最后一个选项。(另见维基百科文章中的“鼻恶魔”。)

问题中的代码(两种变体)已损坏。直到for循环变成惯用的:

for (i = 0; i < 10; i++)

这些程序只是被破坏了,调用了未定义的行为,并且在它们运行时任何事情都可能发生。

如果您愿意,您可以分析为这两个错误程序生成的程序集(但最好不要这样做)。我对行为差异的最佳猜测是,具有恒定长度数组 (CLA) 的版本对数组和索引变量的布局与具有可变长度数组 (VLA) 的版本不同,因此会溢出 VLA 中的数组版本覆盖不同的数据以防止 CLA 版本中的数组溢出。

但是详细调查发生的事情没有什么意义。行为是未定义的,并且知道在一个平台上使用一组编译标志的一个源文件和一个编译器的一个版本会发生什么,不会使代码正确、可靠、可移植或任何其他有用的东西。添加第二个数组可以改变事情;更改编译器版本、使用不同的编译器、迁移到新平台或更改编译的优化级别——任何这些都可能改变所发生的事情,而且你没有人可以责备自己,因为代码被破坏了。问题出在代码上,而不是编译器对它做了什么。

于 2015-01-03T19:03:54.953 回答
0

@Ashalynd:操作系统是 Windows 8.1,我正在使用 CodeBlocks。遗漏的分号是打字错误。@JonathanLeffler:这是否意味着编译器在恒定长度数组和可变长度数组的情况下表现不同?我想知道的是,为什么当我在代码本身中初始化数组大小时代码会表现出特定的行为,为什么当我从用户输入数组大小时它工作得非常正常,即使我的 for 循环从1而不是0?我知道用 1 为数组启动一个 for 循环是一个错误,编译器没有显示,但我的问题是为什么它在我的问题中上面提到的 2 种情况下表现不同?

于 2015-01-05T19:56:47.303 回答