我知道在某些语言中以下内容:
a += b
比起以下方式更加高效:
a = a + b
因为它省去了创建临时变量的需求。这在C语言中是否也是这样?使用+=(因此也包括-=
,*=
等)更高效吗?
我知道在某些语言中以下内容:
a += b
比起以下方式更加高效:
a = a + b
因为它省去了创建临时变量的需求。这在C语言中是否也是这样?使用+=(因此也包括-=
,*=
等)更高效吗?
这里给出一个明确的答案...
$ cat junk1.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s = s + a * a;
}
printf("Final sum: %ld\n", s);
}
michael@isolde:~/junk$ cat junk2.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s += a * a;
}
printf("Final sum: %ld\n", s);
}
michael@isolde:~/junk$ for a in *.c ; do gcc -O3 -o ${a%.c} $a ; done
michael@isolde:~/junk$ time ./junk1
Final sum: 3338615082255021824
real 0m2.188s
user 0m2.120s
sys 0m0.000s
michael@isolde:~/junk$ time ./junk2
Final sum: 3338615082255021824
real 0m2.179s
user 0m2.120s
sys 0m0.000s
这是针对我的电脑、我的编译器和我的操作系统的测试结果。您的结果可能会有所不同。然而,在我的系统上,时间相同:用户时间为2.120秒。
现在,为了展示现代编译器的强大之处,您会注意到我在赋值中使用了表达式a * a
。这是因为存在这个小问题:
$ cat junk.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s = s + a;
}
printf("Final sum: %ld\n", s);
}
michael@isolde:~/junk$ gcc -O3 -S junk.c
michael@isolde:~/junk$ cat junk.s
.file "junk.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Final sum: %ld\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB22:
.cfi_startproc
movabsq $499999999500000000, %rdx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
jmp __printf_chk
.cfi_endproc
.LFE22:
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
编译器发现我的循环并展开它,以计算累加和,并将其作为常量嵌入并直接打印出来,完全跳过任何循环构造。在面对如此聪明的优化器时,你真的认为你能够找到任何有意义的差异来区分这实际上是一个编译器特定的问题,但我希望所有现代编译器都会给出相同的结果。使用Visual Studio 2008:
int main() {
int a = 10;
int b = 30;
a = a + b;
int c = 10;
int d = 50;
c += d;
}
代码行 a = a + b 的反汇编结果如下:
0014139C mov eax,dword ptr [a]
0014139F add eax,dword ptr [b]
001413A2 mov dword ptr [a],eax
代码行 c += d 的反汇编结果如下:
001413B3 mov eax,dword ptr [c]
001413B6 add eax,dword ptr [d]
001413B9 mov dword ptr [c],eax
这是一样的。它们编译成相同的代码。
这取决于 a
是什么。
C 语言中的 a += b
从本质上讲等同于 a = a + b
,只是在抽象的角度中,前一种变体中只对 a
求值一次。如果 a
是一个“纯”值,即对程序行为没有影响,那么无论是效率还是其他方面,a += b
与 a = a + b
在所有方面严格等价。
换句话说,在实际可以自由选择 a += b
和 a = a + b
(也就是它们做相同事情的情况下),它们通常具有完全相同的效率。某些编译器可能会在 a
代表函数调用时(例如)出现困难,但当 a
是一个非易失性变量时,两个表达式生成的机器代码将是相同的。
以另一个例子,如果 a
是一个易失性变量,则 a += b
和 a = a + b
具有不同的行为和效率。然而,由于它们不是等价的,因此您的问题在这种情况下根本不适用。
s[i]->m[j1].k = s[i]->m[jl].k + 23; // Was that a typo?
vs:
s[i]->m[j1].k += 23;
两个好处——我不算少打字。第一个和第二个表达式不同时,就不会有错别字的问题;编译器也不会对复杂表达式进行两次评估。这些日子来说,优化编译器比以前要好得多,所以这可能不会有太大的区别,但是你可能会有更复杂的表达式(例如,在下标中作为另一个翻译单元中定义的函数的一部分进行计算),在这种情况下编译器可能无法避免两次评估表达式:
s[i]->m[somefunc(j1)].k = s[i]->m[somefunc(j1)].k + 23;
s[i]->m[somefunc(j1)].k += 23;
s[i++]->m[j1++].k += 23;
但你不能这样写:
s[i++]->m[j1++].k = s[i]->m[j1].k + 23;
s[i]->m[j1].k = s[i++]->m[j1++].k + 23;
+=
可以使代码更易读和可靠,而没有提及“击键少”这个说法。(更不用说提到那些在语义上+=
和=...+
之间有差异的情况了。) - JUST MY correct OPINIONa += b
a = a + b
除非使用真正古老或写得很差的编译器,否则没有区别,只要a
和b
是普通变量,这两个变量会产生相同的结果。
如果你在处理C ++而不是C,则运算符重载将允许有更实质性的差异。