我遇到了这两段代码:
#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
}
为什么程序在第二种情况下运行完美?即使在第二种情况下,下标也会超过声明的数组大小。
答案 0 :(得分:3)
两个代码段都有数组越界访问,因此您会看到未定义的行为
for(i=1;i<=size;i++)
如果您有大小为10
的数组,则有效索引为0-9
一旦我们说出未定义的行为,那么即使崩溃也可能发生任何事情。如果你说它正在运行那么只是运气好的话你可能没有看到崩溃,但你仍然在访问一些你没有分配的内存
答案 1 :(得分:1)
问题中的代码 - 两种变体 - 展示'undefined behaviour',因此“一切皆有可能”。这包括“程序崩溃”,“程序表现得好像溢出是合法的”和“程序尽最大努力擦除机器清理所有数据”。幸运的是,编译器编写者很少使用最后一个选项。 (另请参阅维基百科文章中的“Nasal Demons”。)
问题中的代码 - 两种变体 - 都被打破了。直到for
循环更改为惯用语:
for (i = 0; i < 10; i++)
程序简单地被破坏,调用未定义的行为,并且当它们运行时会发生任何事情。
如果您愿意,可以根据需要分析为两个故障程序生成的程序集(但最好不要)。我对行为差异的最佳猜测是,具有常量长度数组(CLA)的版本规定了数组和索引变量与具有可变长度数组(VLA)的版本不同,因此在VLA中溢出数组版本会覆盖CLA版本中溢出数组的不同数据。
但调查详细情况并没有什么意义。行为是未定义的,并且知道一个源文件和一个编译器的一个版本在一个平台上具有一组编译标志会发生什么将不会使代码正确或可靠或可移植或其他任何有用的。添加第二个数组可以改变一些事情;更改编译器版本,使用不同的编译器,移动到新平台,或更改编译的优化级别 - 任何这些都可以改变发生的事情,并且你没有人可以责怪你自己,因为代码被破坏了。问题出在代码中,而不是编译器用它做什么。
答案 2 :(得分:0)
@Ashalynd:操作系统是Windows 8.1,我正在使用CodeBlocks。错过的分号是打字错误。 @JonathanLeffler:那么这是否意味着编译器在常量长度数组和可变长度数组的情况下表现不同? 所有我想知道的是,为什么代码在代码本身初始化数组大小时会表现出特定的行为,为什么当我从用户输入数组大小时它完全正常,即使我的for循环从1而不是0? 我知道为一个带1的数组启动一个for循环是一个错误,编译器没有显示,但我的问题是为什么在我的问题中上面提到的2个案例中它的表现不同?