因为很少使用位运算(与或非、移位以及对应的赋值操作),渐生一种神秘感,今天就把这层面纱揭去,看看他们的真面目。由于位运算和逻辑运算都有与或非操作,这里看看他们的区别。
引入这些运算的原因
c++继承自c,c使用位运算自然是为了提高效率,使得c可以像低级语言那样实现bit-level的操作。使用逻辑运算是为了控制程序流程。
现代编译器的优化功能不容小视,下面的汇编代码都是编译器优化后产生的。这些优化有些使我们吃惊,但却解释我们心中的疑惑。其中匿名局部变量处理、常量运算求值和内联函数展开和求值是常见的优化点。
1. 位运算
1.1 位的与或非
参与运算的两个操作数必须为整形或其变体,操作数按位进行与或非操作。我们使用汇编代码给出真相。
void f(){
0xfecb | 0xf03e0;
int a1 = 0xfecb & 0xf03e0 | 0x3c83 & (~0x2db40f85)| ~0xfecb & 0xf03e0 | ~0x3c83 & (~0x2db40f85);
char a2 = 0xf;
int a3 = 0xf0;
int a4 = a2 & a3;
}
对应的汇编代码为:
void f(){
00413C60 push ebp
00413C61 mov ebp,esp
00413C63 sub esp,0F0h
00413C69 push ebx
00413C6A push esi
00413C6B push edi
00413C6C lea edi,[ebp-0F0h]
00413C72 mov ecx,3Ch
00413C77 mov eax,0CCCCCCCCh
00413C7C rep stos dword ptr es:[edi]
0xfecb | 0xf03e0;
int a1 = 0xfecb & 0xf03e0 | 0x3c83 & (~0x2db40f85)| ~0xfecb & 0xf03e0 | ~0x3c83 & (~0x2db40f85);
00413C7E mov dword ptr [a1],0D24FF3FAh
char a2 = 0xf;
00413C85 mov byte ptr [a2],0Fh
int a3 = 0xf0;
00413C89 mov dword ptr [a3],0F0h
int a4 = a2 & a3;
00413C90 movsx eax,byte ptr [a2]
00413C94 and eax,dword ptr [a3]
00413C97 mov dword ptr [a4],eax
}
这个例子说明,0.1 不使用的局部变量,编译器不会为它生成代码。 0.2 常量运算编译器直接求值。 0.3 变量的运算只能在运行时完成,编译器无法为其代劳。 1. 两个长度不同的变量做位运算时,需要符号扩展长度较小的变量,两操作数长度相同再运算。另外,c++所有的位运算都是算术的,这也体现在右移运算中。
1.2 移位运算
移位运算包括左移和右移运算,对操作数的要求同与或非。左移后空出的右边由若干个0填补,右移后空出的左边由若干个符号位填补。