您甚至不需要while,您只需要能够比较并能够分支,换句话说,if 和goto。在下面的程序中,函数test_normally() 和test_subnormally() 是等价的:
#include <stdio.h>
void exec1(void)
{
puts("exec1() called");
}
void exec2(void)
{
puts("exec2() called");
}
void exec3(void)
{
puts("exec3() called");
}
void test_normally(void)
{
int cond1 = 0;
int cond2 = 1;
int i = 5;
/* First if test */
if ( cond1 )
exec1();
else
exec2();
puts("First if test over.");
/* Second if test */
if ( cond2 )
exec1();
else
exec2();
puts("Second if test over.");
/* While test */
while ( i > 0 ) {
exec3();
--i;
}
puts("Loop test over.");
}
void test_subnormally(void)
{
int cond1 = 0;
int cond2 = 1;
int i = 5;
/* First if test */
if ( !cond1 )
goto cond1_false;
exec1();
goto cond1_end;
cond1_false:
exec2();
cond1_end:
puts("First if test over.");
/* Second if test */
if ( !cond2 )
goto cond2_false;
exec1();
goto cond2_end;
cond2_false:
exec2();
cond2_end:
puts("Second if test over.");
/* While test */
loop_start:
if ( !(i > 0) )
goto loop_end;
exec3();
--i;
goto loop_start;
loop_end:
puts("Loop test over.");
}
int main(void)
{
test_normally();
putchar('\n');
test_subnormally();
return 0;
}
和输出:
paul@local:~/src/sandbox$ ./goto
exec2() called
First if test over.
exec1() called
Second if test over.
exec3() called
exec3() called
exec3() called
exec3() called
exec3() called
Loop test over.
exec2() called
First if test over.
exec1() called
Second if test over.
exec3() called
exec3() called
exec3() called
exec3() called
exec3() called
Loop test over.
paul@local:~/src/sandbox$
通过比较这两个函数,希望你能明白为什么if 和while 以及他们所有的朋友都比替代品更好。
test_subnormally() 实际上非常接近处理器的实际工作,在您的 C 源代码编译成机器代码之后。这是在 64 位 Intel 处理器上来自 gcc 的 test_normally() 的汇编输出 - 您可以看到汇编指令和另一个函数 test_subnormally() 的 C 源代码之间几乎存在一对一的映射:
.LC3:
.string "First if test over."
.LC4:
.string "Second if test over."
.LC5:
.string "Loop test over."
.text
.globl test_normally
.type test_normally, @function
test_normally:
.LFB3:
.cfi_startproc # // Function entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, -8(%rbp) # // -8(%rbp) is cond1
movl $1, -12(%rbp) # // -12(%rbp) is cond2
movl $5, -4(%rbp) # // -4(%rbp) is i
cmpl $0, -8(%rbp) # if ( !cond1 )
je .L5 # goto cond1_false;
call exec1 # exec1();
jmp .L6 # goto cond1_end;
.L5: # cond1_false:
call exec2 # exec2();
.L6: # cond1_end:
movl $.LC3, %edi # // move "First if test over" to %edi
call puts # puts("First if test over");
cmpl $0, -12(%rbp) # if ( !cond2 )
je .L7 # goto cond2_false;
call exec1 # exec1();
jmp .L8 # goto cond2_end;
.L7: # cond2_false:
call exec2 # exec2();
.L8: # cond2_end:
movl $.LC4, %edi # // move "Second if test over" to %edi
call puts # puts("Second if test over");
jmp .L9 # goto loop_start;
.L10: # loop_body:
call exec3 # exec3();
subl $1, -4(%rbp) # --i;
.L9: # loop_start:
cmpl $0, -4(%rbp) # if ( !(i > 0) ) ...
jg .L10 # ...goto loop_body;
movl $.LC5, %edi # // move "Loop test over" to %edi
call puts # puts("Loop test over");
leave # // Function exit
.cfi_def_cfa 7, 8
ret
.cfi_endproc
编译器在这里选择了以稍微不同的顺序放置循环片段,但除此之外,它的读取几乎与 test_subnormally() 的 C 源代码完全相同。我们拥有if 和while 以及他们在C 语言中的朋友的根本原因正是为了让我们不必编写看起来像这样的代码,但这种简单程度和类似意大利面条的代码才是处理器最终的目标最终执行(并且是图灵完备所需要的全部),因此我们有一个编译器,可以将人类看起来更易于理解和维护的东西变成处理器非常满意的那种混乱。