循环终止后'i'的值是多少.它始终为零.为什么?

Rok*_*oko 3 c loops

#include<stdio.h>
#include<conio.h>
void main()
{
    int i=1;
    for(;i;)i++;
    printf("%d",i);
    getch();
}
Run Code Online (Sandbox Code Playgroud)

无论i最初的值是多少,我总是将输出设为零.

Ant*_*ala 5

TL; DR:您的程序打印,0因为如果循环退出,它必须是因为i为零; 并且i = 1从这开始可能发生例如有符号整数溢出导致环绕.但是C标准实际上并不需要这样的环绕,因此代码调用未定义的行为.优化编译器可能并且可能会使得到的程序做有趣的事情.


for循环可以用下面的交换while环路-它们是相同的.

while (i) {
    i++;
}
Run Code Online (Sandbox Code Playgroud)

这意味着:"虽然i非零,但增加i1".循环存在后,值i必须为零,即打印的值.

然而,起始值i是1.它碰巧打印0只是一个侥幸,因为你的程序调用未定义的行为:每次迭代你将它递增1.到它成为时INT_MAX,你加1,行为未定义,因为INT_MAX + 1无法表示int.

然后任何事情都可能发生,包括计数器缠绕到INT_MIN,这似乎发生在你的未经优化的程序.但是,它不是必需的.例如,编译器可以生成使循环永久挂起的代码.这不仅仅是假设,但使用GCC 6.3.0很容易生成:

% gcc wraparound.c -O2
wraparound.c: In function ‘main’:
wraparound.c:6:14: warning: iteration 2147483646 invokes undefined behavior 
           [-Waggressive-loop-optimizations]
     for(;i;)i++;
             ~^~
wraparound.c:6:5: note: within this loop
     for(;i;)i++;
     ^~~
Run Code Online (Sandbox Code Playgroud)

由此产生的程序永远不会结束,也不会打印任何东西.

反汇编表明,唯一的指令main是:

0000000000000530 <main>:
 530:   eb fe                   jmp    530 <main>
Run Code Online (Sandbox Code Playgroud)

也就是说,代码运行无限循环,即while (1) {}.


如果 - 由于某种原因 - 它确实包围INT_MIN并继续执行,那么最终负数INT_MIN将增加必要的20亿次以使其变为0,然后终止条件i评估为0,这是一个假值,并且循环终止.而且既然i现在拥有价值0,那就是印刷品.

整数溢出也用作C标准3.4.3未定义行为中的唯一示例:

未定义的行为

  1. 使用不可移植或错误的程序结构或错误数据时的行为,本国际标准不对此要求

  2. 注意可能的未定义行为包括完全忽略具有不可预测结果的情况,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止转换或执行(使用发布诊断消息).

  3. 示例未定义行为的示例是整数溢出的行为.


C标准未定义有符号整数溢出,但保证无符号整数数学包装.如果更改int i = 1;为,您的代码将完全定义unsigned int i = 1;.但是,如果unsigned ints为64位宽而您没有优化,则可能需要等待一段时间.

这个计划

#include <stdio.h>
int main(void)
{
    unsigned int i = 1;
    for(;i;)i++;
    printf("%d\n",i);
}
Run Code Online (Sandbox Code Playgroud)

当我使用我的GCC编译时-O2完全展开循环并立即打印0:

% time ./a.out
0
./a.out  0.00s user 0.00s system 0% cpu 0.003 total
Run Code Online (Sandbox Code Playgroud)

即编译器推断出结果并硬编码.