这个循环如何结束,结果是否确定?

3

我发现了一些代码,对于循环如何退出以及它是如何工作的感到困惑。这个程序是否产生确定性输出?

我感到困惑的原因是:

1. `someArray` is of size 2, but clearly, the loop goes till size 3,
2. The value is deterministic and it always exits `someNumber` reaches 4

有人能解释一下这是怎么发生的吗?

当我在包含库名称时使用尖括号<>,代码打印不正确。

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main() {
    int someNumber = 97;
    int someArray[2] = {0,1};
    int findTheValue;

    for (findTheValue=0; (someNumber -= someArray[findTheValue]) >0; findTheValue++) {

    }
        printf("The crazy value is %d", findTheValue);
    return EXIT_SUCCESS;
}

实际上,我本来以为findTheValue的输出结果应该是0...因为当findTheValue == 0时,someNumber -= someArray[findTheValue]肯定大于0... - Dair
@anon:循环会一直持续,直到条件变为假。 - Brad
它在你的电脑上可能每次都能正常运行,但它并不是确定性的。仅因为它在你运行时工作了,并不意味着这段代码是好的。 - Nick Moore
这段代码读取了数组末尾之后的内存。这是一个错误,使输出变得毫无意义。你可能在你的机器上得到一致的输出,但代码仍然是有问题且无用的。 - Blastfurnace
4个回答

2
访问数组元素越界是未定义行为。也就是说,程序可以随意执行任何操作,回复 42,吃掉您的硬盘或花光所有资金。换句话说,在这种情况下发生的情况完全取决于平台。它可能看起来很“确定性”,但这只是因为你很幸运,而且可能只是因为你只从那个位置读取而不写入。
这种代码是糟糕的。不要这样做。

1
循环退出是因为(someNumber -= someArray [findTheValue])没有设置。
添加一个调试行,你可以看到。
value 0 number 97 array 0
value 1 number 96 array 1
value 2 number 1208148276 array -1208148180

这将打印出 findTheValue、someNumber 和 someArray[findTheValue]。

这不是我一开始预期的答案。


抱歉,但我无法得到您显示的输出。我正在使用64位Ubuntu,gcc版本为4.4.5。 - Jimm
我用我的输出回复了Brad的回复。正如你所看到的,对于someNumber -= someArray [findTheValue]的值总是被设置的,而且我可以在for循环退出后打印出该值。 - Jimm
1
正如其他人指出的那样,问题在于数组[2]中的值实际上是随机的,因此您可能会得到不同的结果。 - BugFinder
你使用的是哪个操作系统和编译器得到了上述答案? - Jimm
Linux是一个开源的操作系统,广泛用于计算机领域。GCC(GNU Compiler Collection)是一个常用的编译器套件,用于将源代码转换为可执行文件。问题在于数组的第三个元素可以指向任何内容。如果你多次运行程序,有可能会再次分配相同的内存块给应用程序,并得到相同的结果。如果你能同时运行多个副本(尽管这是一个快速的应用程序,这使得这个过程更加困难),几乎肯定会得到完全不同的结果,还有编译器对数组处理的方式也会影响结果。 - BugFinder

1

根据您的编译器,someArray[2] 是指向 findTheValue 的指针!

因为这些变量是依次声明的,它们很可能在内存中连续位置(我相信在堆栈上)。C 不会真正进行任何内存管理或错误检查,因此 someArray[2] 只是意味着在 someArray[0] + 2 * sizeof(int) 处的内存。

所以当 findTheValue 为 0 时,我们减去,然后当 findTheValue 为 1 时,我们减去 1。当 findTheValue 为 2 时,我们减去 someNumber(现在为 94)并退出。

这种行为绝不是保证的。不要依赖它!

编辑:更有可能的是,someArray[2] 只是指向 RAM 中的垃圾(未指定)值。这些值很可能大于 93 并导致循环退出。

编辑2:或者someArray [2]someArray [3]是大的负数,减去两个会导致someNumber变成负数。


我已经在我的电脑上运行了这个程序很多次,并且始终得到相同的输出...你确定吗?而且他也是完全相同的输出... - Dair
你可能是对的。我编辑了我的帖子。尝试在循环内打印someArray [findTheValue]的值,告诉我们你看到了什么? - Brad
这是我一直看到的输出:findTheValue = 0,someNumber = 97,someArray [findTheValue] = 0 findTheValue = 1,someNumber = 96,someArray [findTheValue] = 1 findTheValue = 2,someNumber = 94,someArray [findTheValue] = 2。疯狂的值是3。 - Jimm
正如我之前回复的那样,在循环中的第一次迭代执行的是97-0,第二次迭代执行的是97-1,第三次迭代(即使数组大小为2)也会执行,并显示96-2,即94,然后循环退出并将crazyvalue的值显示为3。 - Jimm
但对于其他编译器、其他机器而言,它可能是不同的,并且几乎肯定会给出另一个值。就目前而言,你从代码中得到的那个疯狂的值应该是数组长度,正如人们所期望的那样,然而,正如它所显示的那样,我编译了你的代码,得到了一个不同的结果。它不能产生可靠的答案。 - BugFinder

0

检查地址:

printf("&someNumber =   %p\n", &someNumber);
printf("&someArray[0] = %p\n", &someArray[0]);
printf("&someArray[1] = %p\n", &someArray[1]);
printf("&findTheValue = %p\n", &findTheValue);

输出结果为:

&someNumber =   0xbfc78e5c
&someArray[0] = 0xbfc78e50
&someArray[1] = 0xbfc78e54
&findTheValue = 0xbfc78e58

似乎由于某种原因,编译器将数组放在堆栈区域的开头,然后是下面声明的变量,最后是按照声明顺序从上到下的变量。所以someArray[3]实际上指向someNumber。

我真的不知道原因,但我尝试了Ubuntu 32位上的gcc和带有和不带优化的Visual Studio,结果总是类似的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接