复合赋值操作符有 +=, -=, *=, /=,%=, <<=, >>=, &=, ^=, |=。
下面以+=为例
a += expression (1)
等价于:
a = a + expression (2)
但是,这里是有区别的,(1)式中的a(如数组下标访问的元素)只求值一次,(2)式中的a求值两次,当然编译器也许会优化,可能会使得(2)式效果和(1)式一样,但是有例外的情况,编译器是无法优化的。如:
int f(int x){return x;}
int a[10] = {0};
int x = 2;
a[f(x) + 1] += 1; // (1)
a[f(x) + 1] = a[f(x) +1] +1; // (2)
以上两个表达式机器代码是完全不同的,对于(2)式,由于是调用函数,编译器并不能确定每次函数都返回相同的值(即是否有副作用),所以编译器对此无能为力,不能对此优化,因此也就会去乖乖地调用两次函数,而(1)式只调用一次函数,参见VS2010下的汇编代码:
a[f(x) + 1] = a[f(x) +1] +2;
013B1413 mov eax,dword ptr [ebp-3Ch]
013B1416 push eax
013B1417 call @ILT+110(_f) (13B1073h)
013B141C add esp,4
013B141F mov esi,dword ptr [ebp+eax*4-2Ch]
013B1423 add esi,2
013B1426 mov ecx,dword ptr [ebp-3Ch]
013B1429 push ecx
013B142A call @ILT+110(_f) (13B1073h)
013B142F add esp,4
013B1432 mov dword ptr [ebp+eax*4-2Ch],esi
a[f(x) + 1] += 2;
013B1436 mov eax,dword ptr [ebp-3Ch]
013B1439 push eax
013B143A call @ILT+110(_f) (13B1073h)
013B143F add esp,4
013B1442 lea ecx,[ebp+eax*4-2Ch]
013B1446 mov dword ptr [ebp-104h],ecx
013B144C mov edx,dword ptr [ebp-104h]
013B1452 mov eax,dword ptr [edx]
013B1454 add eax,2
013B1457 mov ecx,dword ptr [ebp-104h]
013B145D mov dword ptr [ecx],eax
因此,如果我们能确定表达式中不含有副作用的元素(如上面的函数f),那么我们应尽量使用复合赋值操作符,不失效率同时书写方便。