是否在内外或在其他地方返回?

时间:2013-07-10 15:13:23

标签: c optimization if-statement

(C)如果我有一个包含if的函数,如果条件为真,则可以返回某个值,然后返回一个不同的值。是否或多或少有效使用别人或不打扰?

即。 ...

int foo (int a) {
    if ((a > 0) && (a < SOME_LIMIT)) {
        b = a //maybe b is some global or something
        return 0;
    } else {
        return 1;
    }
    return 0;
}

或只是

int foo (int a) {
    if ((a > 0) && (a < SOME_LIMIT)) {
        b = a //maybe b is some global or something
        return 0;
    }
    return 1;
}

假设GCC,编译代码中的第一个实现结果是否与第二个实现结果不同?

我需要在这里尽可能高效,因此可能减少其他分支会很好 - 但风格上我的内部OCD不喜欢看到一个非0或无效的返回作为a中的最后一条指令因为它不太清楚正在发生什么。所以,如果它无论如何都会被淘汰,那么我可以将其他东西留在那里......

6 个答案:

答案 0 :(得分:6)

您可以使用-O3 -S选项运行gcc以生成优化的汇编代码,以便您可以查看(和比较)优化的汇编。我对您的源进行了以下更改以使它们编译。

档案 a.c

int b;                                                                         

int foo (int a) {             
    if ((a > 0) && (a < 5000)) {  
        b = a;                                                    
        return 0;                                                        
    } else {                                                                   
        return 1;             
    }                                       
    return 0;                          
}

档案 b.c

int b;                                                                         
int foo (int a) {                                                             
    if ((a > 0) && (a < 5000)) {
        b = a;
        return 0;
    }       
    return 1;                                                                  
}

使用gcc -O3 -S a.c编译 a.c 时,会创建 a.s 文件。在我的机器上它看起来像这样:

               .file      "a.c"
               .text
               .p2align 4,,15
               .globl     foo
               .type      foo, @function
foo:
.LFB0:
               .cfi_startproc
               movl       4(%esp), %edx
               movl       $1, %eax
               leal       -1(%edx), %ecx
               cmpl       $4998, %ecx
               ja         .L2
               movl       %edx, b
               xorb       %al, %al
.L2:
               rep
               ret
               .cfi_endproc
.LFE0:
               .size      foo, .-foo
               .comm      b,4,4
               .ident     "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
               .section   .note.GNU-stack,"",@progbits

使用gcc -O3 -S b.c编译 b.c 时,会创建 b.s 文件。在我的机器上它看起来像这样:

               .file      "b.c"
               .text
               .p2align 4,,15
               .globl     foo
               .type      foo, @function
foo:
.LFB0:
               .cfi_startproc
               movl       4(%esp), %edx
               movl       $1, %eax
               leal       -1(%edx), %ecx
               cmpl       $4998, %ecx
               ja         .L2
               movl       %edx, b
               xorb       %al, %al
.L2:
               rep
               ret
               .cfi_endproc
.LFE0:
               .size      foo, .-foo
               .comm      b,4,4
               .ident     "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
               .section   .note.GNU-stack,"",@progbits

请注意,foo:的汇编实现是相同的。因此,在这种情况下,使用此版本的GCC,编写代码的方式无关紧要。

答案 1 :(得分:1)

检查两个实现之间的目标文件。放置一个汇编标题,例如

 PortionInQuestion: 

然后,它将作为标签显示在您的程序集文件中,您可以看到生成的程序集是如何不同的。它们可能根本没有不同(因为优化),或者它们可能完全不同。没有看到原始程序集,就没有办法告诉编译器如何优化它。

答案 2 :(得分:1)

您将无法访问第一个示例中的最后一个return 0;。我会说你的第二个在风格上至少是因为这个更清楚。同一件事的代码通常是一件好事。

关于性能,你可以查看程序集可执行文件,如果你喜欢这样的东西或分析代码,看看是否有实际的差异。我的赌注无关紧要。

最后,如果您的编译器支持优化标志,请使用它们!

答案 3 :(得分:1)

当然这取决于编译器。我想市场上的每个(不错的)编译器在这两种情况下都会产生完全相同的输出。然而,任何关于优化的书都不鼓励尝试这种微观优化!

选择更易读的表格。

答案 4 :(得分:1)

我会这样写...

int foo (int a) {
    if ((a > 0) && (a < SOME_LIMIT)) {
        b = a //maybe b is some global or something
        return 0;
    } else {
        return 1;
    }
}

整个函数只是一个布尔值,用于调整b的值大于零且小于某个常量。并且if语句决定了回报。无需为函数添加默认返回。默认返回将使if条件无效。

答案 5 :(得分:1)

int foo (int a) {

     /* Nothing to do: get out of here */
    if (a <= 0 || a >= SOME_LIMIT) return 1;

    b = a; // maybe b is some global or something

    return 0;
}

在效率方面几乎没有任何差异(最昂贵的部分是函数调用加上返回)。

对于人类读者来说,最简单的“缩进”代码(如上所述)是最容易阅读和理解的。

顺便说一句 生成的汇编程序对我来说也很简单,完全等同于if (a >0 && a < LIMIT)表单。

        .file   "return.c"
        .text
        .p2align 4,,15
        .globl  foo
        .type   foo, @function
foo:
.LFB0:
        .cfi_startproc
        leal    -1(%rdi), %edx
        movl    $1, %eax
        cmpl    $2998, %edx
        ja      .L2
        movl    %edi, b(%rip)
        xorb    %al, %al
.L2:
        rep
        ret
        .cfi_endproc
.LFE0:
        .size   foo, .-foo
        .globl  b
        .bss
        .align 16
        .type   b, @object
        .size   b, 4
b:
        .zero   4
        .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
        .section        .note.GNU-stack,"",@progbits