我最近开始学习C语言,我正在上一门以C为主题的课程。我目前正在玩循环,我遇到了一些奇怪的行为,我不知道如何解释。

#include <stdio.h>

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%d \n", sizeof(array)/sizeof(int));
  return 0;
}

在我运行Ubuntu 14.04的笔记本电脑上,这段代码没有崩溃。它运行到完成。在我学校运行CentOS 6.6的电脑上,它也运行得很好。在Windows 8.1上,循环永远不会终止。

更奇怪的是,当我将for循环的条件编辑为:I <= 11时,代码只在运行Ubuntu的笔记本电脑上终止。它永远不会在CentOS和Windows中终止。

有人能解释一下内存中发生了什么吗?为什么运行相同代码的不同操作系统会产生不同的结果?

编辑:我知道for循环越界了。我是故意这么做的。我只是不明白在不同的操作系统和计算机上,这种行为是如何不同的。


当前回答

这里有两个错误。int i实际上是一个数组元素,数组[10],就像在堆栈上看到的那样。因为你已经允许索引使数组[10]= 0,循环索引i永远不会超过10。令(i=0;我< 10;我+ = 1)。

正如K&R所说,i++是“糟糕的风格”。它增加了i的大小,而不是1。i++是指针数学,I +=1是代数。虽然这取决于编译器,但对于可移植性来说,这不是一个好的约定。

其他回答

与Java不同,C不做数组边界检查,也就是说,没有ArrayIndexOutOfBoundsException异常,确保数组索引有效的工作留给了程序员。故意这样做会导致未定义的行为,任何事情都可能发生。


对于数组:

int array[10]

索引只在0到9的范围内有效。然而,你正试图:

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

访问数组[10],将条件更改为I < 10

当你遍历i==9时,你把0赋给了实际上位于数组后面的“数组项”,所以你覆盖了一些其他数据。最可能的情况是,你重写了i变量,它位于a[]之后。这样,您只需将i变量重置为零,从而重新启动循环。

如果你在循环中输出i,你自己就会发现:

      printf("test i=%d\n", i);

而不仅仅是

      printf("test \n");

当然,这个结果很大程度上取决于变量的内存分配,而这又取决于编译器及其设置,所以它通常是Undefined Behavior——这就是为什么在不同的机器或不同的操作系统或不同的编译器上的结果可能不同。

漏洞存在于以下代码段之间:

int array[10],i;

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

array[i]=0;

由于数组只有10个元素,在最后一次迭代中数组[10]= 0;是缓冲区溢出。缓冲区溢出是未定义的行为,这意味着它们可能格式化您的硬盘驱动器或导致恶魔从您的鼻子里飞出来。

所有的堆栈变量都是相邻排列的,这是很常见的。如果i位于数组[10]写入的位置,则UB将i重置为0,从而导致未终止循环。

要修复,将循环条件更改为i < 10。

你有一个边界违反,在非终止平台上,我相信你无意中在循环结束时将I设置为0,这样它就会重新开始。

数组[10]无效;它包含10个元素,从数组[0]到数组[9],数组[10]是第11个。你的循环应该在10之前停止,如下所示:

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

数组[10]落在实现定义的位置,有趣的是,在两个平台上,它落在i上,这些平台显然是直接放在数组后面。I被设置为0,循环将永远继续下去。对于其他平台,i可能位于array之前,或者array可能在它之后有一些填充。

除了内存布局的可能性之外,写入[10]的尝试实际上会覆盖i,优化编译器也可能会确定,如果代码没有首先访问不存在的数组元素[10],则i值大于10时无法达到循环测试。

Since an attempt to access that element would be undefined behavior, the compiler would have no obligations with regard to what the program might do after that point. More specifically, since the compiler would have no obligation to generate code to check the loop index in any case where it might be greater than ten, it would have no obligation to generate code to check it at all; it could instead assume that the <=10 test will always yield true. Note that this would be true even if the code would read a[10] rather than writing it.