如果任何体面的编译器没有为这两种可能性发出相同的汇编程序,我会感到惊讶.这是一个编译和运行的简单C程序:
#includeint main(void) { int c = getchar(); if (c == 'y') { ++c; goto end; } else { --c; } end: putchar(c); putchar('\n'); return 0; }
编译gcc -S
,没有优化标志,这里是输出:
.file "goto_skip_41709548.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp call getchar movl %eax, -4(%rbp) cmpl $121, -4(%rbp) jne .L2 addl $1, -4(%rbp) jmp .L3 .L2: subl $1, -4(%rbp) .L3: movl -4(%rbp), %eax movl %eax, %edi call putchar movl $10, %edi call putchar movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4" .section .note.GNU-stack,"",@progbits
编译器为goto
删除的相同代码提供了完全相同的输出.验证diff
:
?> diff w_goto.s wo_goto.s 1c1 < .file "goto_skip_41709548.c" --- > .file "no_goto_skip_41709548.c"
当您的代码编译时,它可能(即在大多数正常的编译器上)转换为类似于以下伪代码的东西:
if (a != true) jump else foo() * will not execute if a is false * because of the `jump else` jump end else: bar() * will not execute if a is true * because of the `jump end` end:
where else
和end
are标签(尝试a
在纸上使用true和false运行它).
基本上,两个片段都将转换为相同的编译版本,并且您goto
将对结果没有影响,因为它包含在分支中.最多,它会使你的编译器再次出汗2毫秒来检测并忽略它.
在考虑性能时,半牵连的手动跳转没有多大意义.突破循环,非重新声明和其他程序员端优化活动贡献更多.
需要注意的是,这种分支是如何构建汇编条件的,因为将逻辑运算存储在寄存器中并进行比较以检查是否跳转.一个更精确的伪代码
cmp a, true /or/ xor a, a ; 0 if true, 1 if false jne else /or/ jnz else ; jump if not equal / zero foo() jmp end ; jump to else: bar() end: