我正在学习编程,有时我会发现使用变量返回使我的代码更可读。
我想知道这些功能是否执行相同的操作并且同样有效。
案例1:
int Foo1()
{
int x = 5 + 6 + 7; // Return variable
return x;
}
int Foo2(int y)
{
return 5 + 6 + 7;
}
在这种情况下,我认为初始化和总和在编译时间发生,因此它们之间没有区别。
情况2:
int Foo1(int y)
{
int x = y + 6 + 7; // Return variable
return x;
}
int Foo2(int y)
{
return y + 6 + 7;
}
但是,在这种情况下会发生什么?看来初始化发生在执行时间,并且必须执行。
比初始化变量直接返回值然后返回该值?我是否应该始终尝试直接返回值而不是使用变量返回?
您可以轻松地自己尝试。
您可以从编译器获得组件
没有优化:
(gcc -S -O0 -o src.S src.c
(
.file "so_temp.c"
.text
.globl case1Foo1
.type case1Foo1, @function
case1Foo1:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $18, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size case1Foo1, .-case1Foo1
.globl case1Foo2
.type case1Foo2, @function
case1Foo2:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $18, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size case1Foo2, .-case1Foo2
.globl case2Foo1
.type case2Foo1, @function
case2Foo1:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movl -20(%rbp), %eax
addl $13, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size case2Foo1, .-case2Foo1
.globl case2Foo2
.type case2Foo2, @function
case2Foo2:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $13, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size case2Foo2, .-case2Foo2
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",@progbits
您可以看到,foo2版本的指令少于函数的foo1版本。
优化变成O3:
(gcc -S -O3 -o src.S src.c
(
.file "so_temp.c"
.text
.p2align 4,,15
.globl case1Foo1
.type case1Foo1, @function
case1Foo1:
.LFB0:
.cfi_startproc
movl $18, %eax
ret
.cfi_endproc
.LFE0:
.size case1Foo1, .-case1Foo1
.p2align 4,,15
.globl case1Foo2
.type case1Foo2, @function
case1Foo2:
.LFB5:
.cfi_startproc
movl $18, %eax
ret
.cfi_endproc
.LFE5:
.size case1Foo2, .-case1Foo2
.p2align 4,,15
.globl case2Foo1
.type case2Foo1, @function
case2Foo1:
.LFB2:
.cfi_startproc
leal 13(%rdi), %eax
ret
.cfi_endproc
.LFE2:
.size case2Foo1, .-case2Foo1
.p2align 4,,15
.globl case2Foo2
.type case2Foo2, @function
case2Foo2:
.LFB7:
.cfi_startproc
leal 13(%rdi), %eax
ret
.cfi_endproc
.LFE7:
.size case2Foo2, .-case2Foo2
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",@progbits
两个版本都是完全相同的。
我仍然不认为这是您应该优化自己的东西。
在这种情况下,应首选可读取的代码,尤其是因为通常不会编译代码并关闭优化的代码。
案例2更有效,但通常不需要,因为编译器极有可能将案例1优化为案例2。
。如果不损害性能(如在这种情况下(。
任何至少适度质量的编译器都将在优化级别(例如GCC的-O1
(中,将其编译为相同的代码。在大多数情况下,您可以轻松看到的任何正确优化将由一个好的编译器执行。
C标准不需要编译器将代码无意识地编译到执行C源代码中确切步骤的说明中。它仅要求编译器生成具有相同效果的代码。这些效果是根据可观察行为来定义的,其中包括程序的输出,与用户的交互以及对挥发对象的访问(您将稍后将学习的特殊对象(。只要不改变可观察的行为,编译器就会消除中间变量之类的东西。