在C语言中,为什么n++
的执行速度比n=n+1
快?
(int n=...; n++;)
(int n=...; n=n+1;)
我们的老师在今天的班上问了这个问题。 (这不是作业)
答案 0 :(得分:102)
如果您正在使用“stone-age”编译器,那将是真的......
如果“石器时代”:
++n
的速度比n++
快n=n+1
机器通常有increment x
以及add const to x
n++
,您将只有2个内存访问权限(读取n,包含,写入n)n=n+1
,您将有3个内存访问权限(读取n,读取const,添加n和const,写入n) 但是今天的编译器会自动将n=n+1
转换为++n
,它会比您想象的更多!
同样在今天的无序处理器上 - 尽管“stone-age”编译器 - 运行时的情况在很多情况下可能不会受到影响 !
答案 1 :(得分:41)
在G86 4.4.3 for x86上,使用或不使用优化,它们编译为完全相同的汇编代码,因此执行时间相同。正如您在程序集中看到的那样,GCC只需将n++
转换为n=n+1
,然后将其优化为单指令添加(在-O2中)。
您的教师建议n++
更快只适用于非常古老的非优化编译器,这些编译器不够智能,无法为n = n + 1
选择就地更新说明。这些编译器已经在PC世界中淘汰了多年,但仍然可以用于奇怪的专有嵌入式平台。
C代码:
int n;
void nplusplus() {
n++;
}
void nplusone() {
n = n + 1;
}
输出程序集(无优化):
.file "test.c"
.comm n,4,4
.text
.globl nplusplus
.type nplusplus, @function
nplusplus:
pushl %ebp
movl %esp, %ebp
movl n, %eax
addl $1, %eax
movl %eax, n
popl %ebp
ret
.size nplusplus, .-nplusplus
.globl nplusone
.type nplusone, @function
nplusone:
pushl %ebp
movl %esp, %ebp
movl n, %eax
addl $1, %eax
movl %eax, n
popl %ebp
ret
.size nplusone, .-nplusone
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
输出程序集(带有-O2优化):
.file "test.c"
.text
.p2align 4,,15
.globl nplusplus
.type nplusplus, @function
nplusplus:
pushl %ebp
movl %esp, %ebp
addl $1, n
popl %ebp
ret
.size nplusplus, .-nplusplus
.p2align 4,,15
.globl nplusone
.type nplusone, @function
nplusone:
pushl %ebp
movl %esp, %ebp
addl $1, n
popl %ebp
ret
.size nplusone, .-nplusone
.comm n,4,4
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
答案 2 :(得分:14)
编译器会将n + 1
优化为虚无。
您的意思是n = n + 1
吗?
如果是这样,他们将编译成相同的程序集。 (假设优化正在进行,并且它们是语句,而不是表达式)
答案 3 :(得分:5)
谁说它呢?你的编译器将它全部优化掉了,真的,使它成为一个没有实际意义的点。
答案 4 :(得分:3)
现代编译器应该能够将两种形式识别为等效形式,并将它们转换为最适合目标平台的格式。此规则有一个例外:具有副作用的变量访问。例如,如果n
是一些内存映射的硬件寄存器,那么从中读取并写入它可能不仅仅是传输数据值(例如,读取可能会清除中断)。您可以使用volatile
关键字让编译器知道优化对n
的访问需要注意,在这种情况下,编译器可能会生成来自n++
的不同代码(增量操作) )和n = n + 1
(读取,添加和存储操作)。但是对于普通变量,编译器应该将两个表单优化为同一个。
答案 5 :(得分:2)
它并不是真的。编译器将根据目标体系结构进行更改。像这样的微优化通常具有可疑的好处,但重要的是,当然不值得程序员的时间。
答案 6 :(得分:2)
实际上,原因在于后置修复程序的运算符定义与预定义不同。 ++n
将增加“n”并返回对“n”的引用,而n++
将增加“n”将返回“{”的const
副本。因此,短语n = n + 1
将更有效率。但我不得不同意上述海报。好的编译器应该优化掉一个未使用的返回对象。
答案 7 :(得分:2)
在C语言中,n++
表达式的副作用按照定义等同于n = n + 1
表达式的副作用。由于您的代码仅依赖于副作用,因此很明显正确答案是这些表达式始终具有完全相同的性能。 (无论编译器中的任何优化设置如何,BTW,因为该问题与任何优化完全无关。)
只有当编译器故意(并且恶意地!)试图引入这种分歧时,才能实现这些表达式的任何实际性能差异。但在这种情况下,它可以采用任何一种方式,当然,无论编译者的作者想要歪曲它。
答案 8 :(得分:1)
我认为它更像是硬件问题而不是软件...如果我记得核心,在较旧的CPU中,n = n + 1需要两个内存位置,其中++ n只是一个微控制器命令......但我怀疑这适用于现代建筑......
答案 9 :(得分:0)
所有这些都取决于编译器/处理器/编译指令。所以做出任何假设“一般来说更快”并不是一个好主意。