C语言编码:临时局部变量的效率

C语言编码:临时局部变量的效率,c,function,variables,coding-style,C,Function,Variables,Coding Style,我在想:用C编程,假设我们有两个函数: int get_a_value(); int calculate_something(int number); 第三个的两个版本: /* version 1 */ int main() { int value = get_a_value(); int result = calculate_something(value); return result; } /* version 2 */ int main() { int

我在想:用C编程,假设我们有两个函数:

int get_a_value();
int calculate_something(int number);
第三个的两个版本:

/* version 1 */
int main()
{
    int value = get_a_value();
    int result = calculate_something(value);
    return result;
}

/* version 2 */
int main()
{
    int result = calculate_something(get_a_value());
    return result;
}
从理论上讲,同一事物的这两个版本在正确性、内存使用和效率方面有什么不同?它们会生成不同的指令吗?另一方面,什么情况会使可能的差异在现实中变得显著


提前感谢。

我执行了一个小测试,为这两个版本生成汇编代码。简单地从bash运行diff命令表明,第一个版本比第二个版本多2条指令。
如果您想自己尝试,只需使用此命令编译即可

gcc -S main.c -o asmout.s
gcc -S main2.c -o asmout2.s
然后用

diff asmout.s asmout2.s
我得到了以下两个说明,第一个是:

movl    %eax, -8(%rbp)
movl    -8(%rbp), %eax
编辑:

正如Keith Thompson所建议的,如果使用优化选项编译,生成的汇编代码对于两个版本都是相同的。

我执行了一个小测试,为两个版本生成汇编代码。简单地从bash运行diff命令表明,第一个版本比第二个版本多2条指令。
如果您想自己尝试,只需使用此命令编译即可

gcc -S main.c -o asmout.s
gcc -S main2.c -o asmout2.s
然后用

diff asmout.s asmout2.s
我得到了以下两个说明,第一个是:

movl    %eax, -8(%rbp)
movl    -8(%rbp), %eax
编辑:

正如Keith Thompson所建议的,如果使用优化选项编译,生成的汇编程序代码对于两个版本都是相同的。

这实际上取决于平台和编译器,但如果使用优化选项,它们通常会生成相同的代码。在最坏的版本中,一个会为一个额外的int分配空间。如果将get_a_value的值放在一个变量中使代码更具可读性,那么我会继续这样做。我唯一建议不要这样做的时候是在一个深度递归函数中。

这实际上取决于平台和编译器,但在进行优化后,它们通常会生成相同的代码。在最坏的版本中,一个会为一个额外的int分配空间。如果将get_a_value的值放在一个变量中使代码更具可读性,那么我会继续这样做。我唯一建议不要这样做的时候是在一个深度递归函数中。

复制了两个版本,分别用
gcc-S
编译以获得机器语言输出,使用
sdiff
进行并排比较

使用gcc版本4.1.2 20070115(SUSE Linux)的结果:

无优化:

main:                                         main:
.LFB2:                                        .LFB2:
        pushq   %rbp                                  pushq   %rbp
.LCFI0:                                       .LCFI0:
        movq    %rsp, %rbp                            movq    %rsp, %rbp
.LCFI1:                                       .LCFI1:
        subq    $16, %rsp                             subq    $16, %rsp
.LCFI2:                                       .LCFI2:
        movl    $0, %eax                              movl    $0, %eax
        call    get_a_value                           call    get_a_value
        movl    %eax, -8(%rbp)              |         movl    %eax, %edi
        movl    -8(%rbp), %edi              <
        movl    $0, %eax                              movl    $0, %eax
        call    calculate_something                   call    calculate_something
        movl    %eax, -4(%rbp)                        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax                        movl    -4(%rbp), %eax
        leave                                         leave
        ret                                           ret
main:                                         main:
        pushl   %ebp                                  pushl   %ebp
        movl    %esp, %ebp                            movl    %esp, %ebp
        subl    $8, %esp                              subl    $8, %esp
                                             >        subl    $12, %esp
                                             >        subl    $4, %esp
        call    get_a_value                           call    get_a_value
                                             >        addl    $4, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -4(%ebp)               |        pushl   %eax
        subl    $12, %esp                    <
        pushl   -4(%ebp)                     <
        call    calculate_something                   call    calculate_something
        addl    $16, %esp                             addl    $16, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -8(%ebp)               |        movl    %eax, -4(%ebp)
        movl    -8(%ebp), %eax               |        movl    -4(%ebp), %eax
        movl    %eax, %eax                            movl    %eax, %eax
        leave                                         leave
        ret                                           ret
没有区别

使用gcc版本2.96 20000731(Red Hat Linux 7.2 2.96-112.7.2)的结果:

无优化:

main:                                         main:
.LFB2:                                        .LFB2:
        pushq   %rbp                                  pushq   %rbp
.LCFI0:                                       .LCFI0:
        movq    %rsp, %rbp                            movq    %rsp, %rbp
.LCFI1:                                       .LCFI1:
        subq    $16, %rsp                             subq    $16, %rsp
.LCFI2:                                       .LCFI2:
        movl    $0, %eax                              movl    $0, %eax
        call    get_a_value                           call    get_a_value
        movl    %eax, -8(%rbp)              |         movl    %eax, %edi
        movl    -8(%rbp), %edi              <
        movl    $0, %eax                              movl    $0, %eax
        call    calculate_something                   call    calculate_something
        movl    %eax, -4(%rbp)                        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax                        movl    -4(%rbp), %eax
        leave                                         leave
        ret                                           ret
main:                                         main:
        pushl   %ebp                                  pushl   %ebp
        movl    %esp, %ebp                            movl    %esp, %ebp
        subl    $8, %esp                              subl    $8, %esp
                                             >        subl    $12, %esp
                                             >        subl    $4, %esp
        call    get_a_value                           call    get_a_value
                                             >        addl    $4, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -4(%ebp)               |        pushl   %eax
        subl    $12, %esp                    <
        pushl   -4(%ebp)                     <
        call    calculate_something                   call    calculate_something
        addl    $16, %esp                             addl    $16, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -8(%ebp)               |        movl    %eax, -4(%ebp)
        movl    -8(%ebp), %eax               |        movl    -4(%ebp), %eax
        movl    %eax, %eax                            movl    %eax, %eax
        leave                                         leave
        ret                                           ret
main:main:
推送%ebp推送%ebp
移动%esp,%ebp移动%esp,%ebp
子项$8,%esp子项$8,%esp
>12美元,特别是
>次级$4,%esp
调用获取值调用获取值
>加4美元,特别是
移动%eax,%eax移动%eax,%eax
移动%eax,-4%(ebp)|推送%eax
12美元,特别是<
PUSH-4(%ebp)<
呼叫计算某物呼叫计算某物
加成$16%esp加成$16%esp
移动%eax,%eax移动%eax,%eax
movl%eax,-8(%ebp)| movl%eax,-4(%ebp)
movl-8(%ebp),%eax | movl-4(%ebp),%eax
移动%eax,%eax移动%eax,%eax
离开
ret-ret
指令数量大致相同,顺序略有不同

一级优化(-O1):

main:main:
推送%ebp推送%ebp
移动%esp,%ebp移动%esp,%ebp
次级美元8%,特别是|次级美元24%,特别是
调用获取值调用获取值
次级贷款$12%,esp |动产%eax,(%esp)
pushl%eax<
呼叫计算某物呼叫计算某物
离开
ret-ret
看起来第二个版本保留了更多的堆栈空间

因此,对于这个使用这些特定编译器的特定示例,这两个版本之间没有太大的差异。在这种情况下,我倾向于第一个版本,原因如下:

  • 更容易在调试器中跟踪;您可以检查从
    get_a_value
    返回的值,然后将其传递给
    calculate_something
  • 它为您提供了一个进行健全性检查的场所,以防
    计算某些东西对于某些输入来说表现不好
    
  • 这对眼睛比较容易 请记住,简练并不一定意味着快速或高效,在一个特定的编译器/硬件组合下快速/高效的东西在另一个编译器/硬件组合下可能会被彻底破坏。一些编译器实际上可以更轻松地优化以清晰方式编写的代码

    您的代码应按顺序为:

  • 正确-无论它的速度有多快,或者如果它不满足它的要求,它使用的内存有多少
  • 安全-无论它的速度有多快,也不管它使用的内存有多少,如果它是恶意软件载体或有暴露的风险