当实现一个无限循环时,在C语言中使用while(1) vs for(;;) vs goto,有什么不同吗?

37

实现无限循环时,使用while(1)for(;;)goto有什么区别吗?

谢谢, Chenz


1
我总是使用while循环。我认为用for循环构造没有什么意义。这只是个人喜好。(对我来说,它似乎缺少信息,而while循环在美观上更加令人满意) - Tim
3
哪个无限循环比另一个更快? - Anonym
3
循环在C语言中有for循环和while循环两种,哪种更好?在大多数情况下,这两种循环是等效的,选择哪一种通常取决于个人喜好和具体情况。for循环通常用于已知循环次数的情况,而while循环则更适合未知循环次数的情况。不过,在某些情况下,使用其中一种循环可能会使代码更加易读或者更加高效。 - anon
14
使用for(;;)可以让你定义#define ever (;;)并使用for ever {...} ;) - kennytm
2
@KennyTM:这很有趣,但另一方面也会误导人。通常,“无限”循环并不会真的永远持续下去,而是直到被“break”才停止。 - chiccodoro
显示剩余4条评论
8个回答

60

它们是等价的,即使你关闭优化器。

例如:

#include <stdio.h>

extern void f(void) {
    while(1) {
        putchar(' ');
    }
}

extern void g(void) {
    for(;;){
        putchar(' ');
    }
}

extern void h(void) {
    z:
        putchar(' ');
    goto z;
}

使用gcc -O0编译会使得这3个函数的汇编代码等价:

 f:
 ;  [ EXTERNAL ]
 ;
 +00000 00000fb4 80402DE9             stmdb             sp!,{r7,lr}
 +00004 00000fb8 00708DE2             add               r7,sp,#0x0
 +00008 00000fbc 2000A0E3 loc_000008: mov               r0,#0x20
 +0000c 00000fc0 0A0000EB             bl                putchar (stub)
 +00010 00000fc4 FCFFFFEA             b                 loc_000008
 ;
 ;
 g:
 ;  [ EXTERNAL ]
 ;
 +00000 00000fc8 80402DE9             stmdb             sp!,{r7,lr}
 +00004 00000fcc 00708DE2             add               r7,sp,#0x0
 +00008 00000fd0 2000A0E3 loc_000008: mov               r0,#0x20
 +0000c 00000fd4 050000EB             bl                putchar (stub)
 +00010 00000fd8 FCFFFFEA             b                 loc_000008
 ;
 ;
 h:
 ;  [ EXTERNAL ]
 ;
 +00000 00000fdc 80402DE9             stmdb             sp!,{r7,lr}
 +00004 00000fe0 00708DE2             add               r7,sp,#0x0
 +00008 00000fe4 2000A0E3 loc_000008: mov               r0,#0x20
 +0000c 00000fe8 000000EB             bl                putchar (stub)
 +00010 00000fec FCFFFFEA             b                 loc_000008

4
用什么命令从目标文件生成了这个特定汇编输出? - bzeaman
@bzeaman,请查看我的答案这里,了解如何在g++和clang++中执行此操作以及如何添加注释以便您知道要查看汇编的哪个部分... - Andrew

9

我刚刚比较了gcc的未优化汇编输出:

# cat while.c 
int main() {
    while(1) {};
    return 0;
}

# cat forloop.c 
int main() {
    for (;;) { };
    return 0;
}

使汇编代码输出:
# gcc -S while.c 
# gcc -S forloop.c 

比较汇编文件:

# diff forloop.s while.s
1c1
<   .file   "forloop.c"
---
>   .file   "while.c"

正如您所看到的,没有明显的差异。以下是输出结果:

# cat while.s 
    .file   "while.c"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
.L2:
    jmp .L2                    # this is the loop in both cases
    .size   main, .-main
    .ident  "GCC: (GNU) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

虽然这并不是技术上的证明它们是相同的,但我认为在99.9%的情况下它们是相同的。


5

生成的汇编代码几乎没有区别。这更多是一个风格问题:

Goto - 简直难看:向后跳转,没有明确的无限循环块

while(1) - 更好,需要“虚拟”条件,但你经常会被编译器(警告级别4)或静态分析工具警告

for(;;) 可能不是最漂亮的,但在我看来最适合,因为这个结构不能有任何其他意义(与while相比)。但有些人出于“相同”的原因更喜欢while(1)...


4
尽管其他帖子中提到没有显着的区别,但使用for (;;)而不是while (1)的常见原因是静态分析工具(以及某些具有特定警告级别的编译器)经常会抱怨while循环。
Goto有点讨厌,但应该会产生与其他方法相同的代码。个人而言,我坚持使用for (;;)(以使Lint满意),但我对while (1)没有任何问题。

4

while(1)for(;;) 是完全等价的,这两种写法都是编写无限循环的常用习语。

我建议避免使用 goto:如果需要跳出无限循环或继续下一次迭代,请使用 breakcontinue


3

没有固定要求。使用最容易理解的方式即可。


2
在C语言中,true的实现方式取决于编译器。
#define TRUE 1

或者
#define TRUE (-1)

AND false被实现为

#define FALSE 0

while (1)等同于while (true),因为0被认为是假。

while (1) == for (; ;),因为它们没有停止条件。

在汇编语言中,它被翻译为:

:loop
  ...
  ...
  ...
  goto loop

所以,如果汇编代码中没有retexit指令,它被视为无限循环。

0

从我回忆起的“反汇编年代”来看,这不会有太大的区别(编译器足够聪明)。在我看来,这更多是关于美学方面的。


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