我正在学习编程,有时我发现使用变量返回使我的代码更具可读性。
我想知道这些功能是否执行相同的操作并且是否同样有效。
案例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;
}
但是,在这种情况下会发生什么?似乎初始化发生在执行时,必须执行。
直接返回值比初始化变量然后返回它快吗?我是否应该总是尝试直接返回值,而不是使用变量返回?
答案 0 :(得分:3)
您可以轻松地自己尝试。
您可以从编译器获取程序集
未经优化:
(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
两个版本完全相同。
我仍然不认为这是您应该优化自己的事情。
在这种情况下,应首选可读性强的代码,尤其是通常在不关闭优化功能的情况下编译代码。
答案 1 :(得分:1)
案例2效率更高,但是通常不需要,因为编译器极有可能将案例1优化为案例2。
如果不影响性能(在这种情况下),请提高可读性。
答案 2 :(得分:1)
任何质量至少中等的编译器,即使在较低的优化水平下(例如GCC的-O1
),也将compile these to the same code。在大多数情况下,您可以轻松看到的任何正确优化都将由一个好的编译器执行。
C标准不需要编译器将代码无意识地编译为执行C源代码中确切步骤的指令。它仅要求编译器产生具有相同效果的代码。这些效果是根据可观察到的行为来定义的,其中包括程序的输出,与用户的交互以及对易失性对象(以后将要学习的特殊对象)的访问。编译器将消除中间变量之类的东西,只要它们可以这样做而不会改变可观察的行为。