rob*_*ntw 11 c variable-assignment assignment-operator
我在某些语言中知道以下内容:
a += b
Run Code Online (Sandbox Code Playgroud)
比以下更有效:
a = a + b
Run Code Online (Sandbox Code Playgroud)
因为它不需要创建临时变量.这是C的情况吗?使用+ =更高效(因此也是-= *=等)
JUS*_*ION 36
所以这是一个明确的答案......
$ 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
Run Code Online (Sandbox Code Playgroud)
...为我的计算机和我的编译器在我的操作系统上运行.您的结果可能会也可能不会发生变化 但是,在我的系统上,时间是相同的:用户时间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
Run Code Online (Sandbox Code Playgroud)
编译器找出了我的循环,并将其展开到计算累积和的位置,并将其作为一个常量嵌入到它打印输出中,完全跳过任何类型的循环结构.面对聪明的优化者,你真的认为你会在区分s = s + a和s += a?之间找到任何有意义的优势吗?
Joh*_*son 22
这确实是一个编译器特定的问题,但我希望所有现代编译器都会给出相同的结果.使用Visual Studio 2008:
int main() {
int a = 10;
int b = 30;
a = a + b;
int c = 10;
int d = 50;
c += d;
}
Run Code Online (Sandbox Code Playgroud)
线a = a + b具有反汇编
0014139C mov eax,dword ptr [a]
0014139F add eax,dword ptr [b]
001413A2 mov dword ptr [a],eax
Run Code Online (Sandbox Code Playgroud)
线c + = d具有反汇编
001413B3 mov eax,dword ptr [c]
001413B6 add eax,dword ptr [d]
001413B9 mov dword ptr [c],eax
Run Code Online (Sandbox Code Playgroud)
哪个是一样的.它们被编译成相同的代码.
AnT*_*AnT 17
这取决于什么a.
a += b在C中,根据定义等同于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?
Run Code Online (Sandbox Code Playgroud)
VS:
s[i]->m[j1].k += 23;
Run Code Online (Sandbox Code Playgroud)
两个好处 - 我不打算减少打字.毫无疑问,当第一和第二种表达方式不同时是否存在拼写错误; 并且编译器不会两次计算复杂表达式.这些天有可能不会产生太大的影响(优化编译器比以前好很多),但你可能还有更复杂的表达式(评估另一个翻译单元中定义的函数,例如,作为其中一部分)下标)编译器可能无法避免两次评估表达式:
s[i]->m[somefunc(j1)].k = s[i]->m[somefunc(j1)].k + 23;
s[i]->m[somefunc(j1)].k += 23;
Run Code Online (Sandbox Code Playgroud)
此外,你可以写(如果你是勇敢的):
s[i++]->m[j1++].k += 23;
Run Code Online (Sandbox Code Playgroud)
但你不能写:
s[i++]->m[j1++].k = s[i]->m[j1].k + 23;
s[i]->m[j1].k = s[i++]->m[j1++].k + 23;
Run Code Online (Sandbox Code Playgroud)
(或任何其他排列)因为评估顺序没有定义.