本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第2章,建议16-1,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
建议16-1:尽量使用复合赋值运算符
C语言不但提供了最基本的赋值运算符“=”,而且为了简化程序并提高编译效率,还允许在赋值运算符“=”之前加上其他运算符,这样就构成了复合赋值运算符(即 /=、*=、%=、+=、-=、<<=、>>=、&=、^= 与 |= )。示例代码如下:
a/=b; /* 等价于 a=a/b; */
a*=b; /* 等价于 a=a*b; */
a%=b; /* 等价于 a=a%b; */
a+=b; /* 等价于 a=a+b; */
a-=b; /* 等价于 a=a-b; */
a<<=b; /* 等价于 a=a<<b; */
a>>=b; /* 等价于 a=a>>b; */
a&=b; /* 等价于 a=a&b; */
a^=b; /* 等价于 a=a^b; */
a|=b; /* 等价于 a=a|b; */
a+=2+1; /* 等价于 a=a+(2+1); */
a/=2*10-5; /* 等价于 a=a/(2*10-5); */
对于表达式a/=b与a=a/b,有读者会问,它们两者之间究竟有什么区别呢?
答案很简单,对于a=a/b,a需要求值两次;而对于a/=b,a仅需要求值一次。一般来说,这种区别对程序的运行没有多大影响。编译器一般会进行优化,使两种表达式的运行效果
一样。
当然,也有例外的情况,这时编译器无法进行优化。当表达式作为函数的返回值时,函数就会被调用两次,例如:
int f(int x)
{
return x;
}
int main (void)
{
int a[10] = {0};
int x = 2;
a[f(x) + 1] += 1;
a[f(x) + 1] = a[f(x) +1] +1;
return 0;
}
在上
面```
的代码中,由于语句“a[f(x) + 1] = a[f(x) +1] +1”是调用函数,编译器并不能确定每次函数调用都返回相同的值,所以编译器对此也无能为力,无法进行优化,因此只好乖乖地调用两次函数;而语句“a[f(x) + 1] += 1”则只调用一次函数。
上面的代码在Microsoft Visual Studio 2010集成开发环境VC++的Debug模式下将生成如下汇编代码:
a[f(x) + 1] += 1;
013F3763 mov eax,dword ptr [ebp-3Ch]
013F3766 push eax
013F3767 call f (13F11DBh)
013F376C add esp,4
013F376F lea ecx,[ebp+eax*4-2Ch]
013F3773 mov dword ptr [ebp-104h],ecx
013F3779 mov edx,dword ptr [ebp-104h]
013F377F mov eax,dword ptr [edx]
013F3781 add eax,1
013F3784 mov ecx,dword ptr [ebp-104h]
013F378A mov dword ptr [ecx],eax
a[f(x) + 1] = a[f(x) +1] +1;
013F378C mov eax,dword ptr [ebp-3Ch]
013F378F push eax
013F3790 call f (13F11DBh)
013F3795 add esp,4
013F3798 mov esi,dword ptr [ebp+eax*4-2Ch]
013F379C add esi,1
013F379F mov ecx,dword ptr [ebp-3Ch]
013F37A2 push ecx
013F37A3 call f (13F11DBh)
013F37A8 add esp,4
013F37AB mov dword ptr [ebp+eax*4-2Ch],esi
从上面的汇编代码中可以很清楚地看到两者之间的区别,使用普通的赋值运算符会加大程序的开销,从而使程序的效率降低。因此,如果能确定表达式中不含具有副作用的元素(如上面的函数f),那么应该尽量使用复合赋值运算符来代替普通的赋值运算符,不过一定要注意程序的可读性。