本节必须掌握的知识点:
位运算
示例十七
代码分析
汇编解析
5.2.1 位运算
位运算符如表5-2所示:
运算符 | 作用 | 示例 |
& | 按位与 | 两个操作数同时为1,结果为1; |
| | 按位或 | 两个操作数只要有一个为1,结果就为1; |
~ | 按位非 | 操作数为1,结果为0;操作数为0,结果就为1; |
^ | 按位异或 | 两个操作数相同,结果为0;不相同结果为1; |
<< | 左移 | 右侧空位补0 |
>> | 右移 | 左侧空位补符号位 |
>> | 无符号右移 | 左侧空位补0 |
表5-2位运算符
所有的位运算只适用于char、short、int、unsigned char、unsigned short、unsigned int整型数据类型。位运算需要按位表示,例如:unsigned char c = 07h。
把变量c按位表示:【最左侧为最高位第7位,最右侧为最低位第0位】。
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
■&按位与运算
语法格式:expr1 & expr2;两个操作数同时为1,结果为1。
- | 按位或运算
语法格式:expr 1 | expr2;两个操作数只要有一个为1,结果就为1。
■~按位非运算
语法格式:~expr 1;操作数为1,结果为0;操作数为0,结果就为1。
■^按位异或运算
语法格式:expr 1 ^ expr 2 ;两个操作数相同,结果为0;不相同结果为1;
■<<左移运算
0x01<<1左移表示,所有的位向左移一位,右侧补0
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
向左移一位
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
0xEF<<3左移表示,所有的位向左移三位,右侧补0
1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
向左移三位
0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
■>>右移运算
0x01>>1右移表示,所有的位向左移一位,左侧补0
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
向右移一位
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
有符号数0xEF>>3右移表示,所有的位向左移一位,有符号左侧补符号位。
1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
向右移三位
1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
5.2.2 示例十七
/*
位运算符
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char c1 = 0x01;//十六进制数
char c2 = 0xEF;
printf("%d\n", c1 & c2);//与运算
printf("%d\n", c1 | c2);//或运算
printf("%d\n", ~c1); //非运算
printf("%d\n", c1 ^ c2);//异或运算
printf("%d\n", c1 << 1);//左移
printf("%d\n", c2 >> 3);//右移
system("pause");
return 0;
}
●输出结果:
1
-17
-2
-18
2
-3
5.2.3 代码分析
示例代码非常简单,分别输出两个char类型变量c1和c2与运算、或运算、非运算、异或运算、左移和右移的结果。
5.2.4 汇编解析
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
c1 sbyte 1
c2 sbyte 0EFh
.const
szMsg db "%d",0dh,0ah,0
.code
start:
movsx eax,sbyte ptr c1
movsx ebx,sbyte ptr c2
and eax,ebx
invoke printf,offset szMsg,eax;输出结果
;
movsx eax,sbyte ptr c1
movsx ebx,sbyte ptr c2
or eax,ebx
invoke printf,offset szMsg,eax;输出结果
;
movsx eax,sbyte ptr c1
not eax
invoke printf,offset szMsg,eax;输出结果
;
movsx eax,sbyte ptr c1
movsx ebx,sbyte ptr c2
xor eax,ebx
invoke printf,offset szMsg,eax;输出结果
;
movsx eax,sbyte ptr c1
shl eax,1
invoke printf,offset szMsg,eax;输出结果
;
movsx eax,sbyte ptr c2
sar eax,3
invoke printf,offset szMsg,eax;输出结果
;
invoke _getch
ret
end start
输出结果:
1
-17
-2
-18
2
-3
上述汇编代码需要关注以下几点:
变量c1和c2的数据类型为sbyte 8位有符号整型。因为printf函数的参数入栈为32位,因此需要使用movsx指令将变量c1和c2的符号位扩展为32位,然后再进行位运算。汇编指令and与运算,or或运算,not非运算,xor异或运算。
接下来的移位指令shl为左移指令,将eax的所有数据位左移1位。sar右移指令为有符号数右移指令,将eax的所有数据位右移3位,左侧最高位填充符号位。
【注意】masm32汇编器不支持0x作为前缀的十六进制数格式,改用后缀h表示十六进制数。
■反汇编代码
char c1 = 0x01;//十六进制数
00FA1838 mov byte ptr [c1],1
char c2 = 0xEF;
00FA183C mov byte ptr [c2],0EFh
printf("%d\n", c1 & c2);//与运算
00FA1840 movsx eax,byte ptr [c1]
00FA1844 movsx ecx,byte ptr [c2]
00FA1848 and eax,ecx
00FA184A push eax
00FA184B push offset string "%d\n" (0FA7B30h)
00FA1850 call _printf (0FA104Bh)
00FA1855 add esp,8
printf("%d\n", c1 | c2);//或运算
00FA1858 movsx eax,byte ptr [c1]
00FA185C movsx ecx,byte ptr [c2]
00FA1860 or eax,ecx
00FA1862 push eax
00FA1863 push offset string "%d\n" (0FA7B30h)
00FA1868 call _printf (0FA104Bh)
00FA186D add esp,8
printf("%d\n", ~c1); //非运算
00FA1870 movsx eax,byte ptr [c1]
00FA1874 not eax
00FA1876 push eax
00FA1877 push offset string "%d\n" (0FA7B30h)
00FA187C call _printf (0FA104Bh)
00FA1881 add esp,8
printf("%d\n", c1 ^ c2);//异或运算
00FA1884 movsx eax,byte ptr [c1]
00FA1888 movsx ecx,byte ptr [c2]
00FA188C xor eax,ecx
00FA188E push eax
00FA188F push offset string "%d\n" (0FA7B30h)
00FA1894 call _printf (0FA104Bh)
00FA1899 add esp,8
printf("%d\n", c1 << 1);//左移
00FA189C movsx eax,byte ptr [c1]
printf("%d\n", c1 << 1);//左移
00FA18A0 shl eax,1
00FA18A2 push eax
00FA18A3 push offset string "%d\n" (0FA7B30h)
00FA18A8 call _printf (0FA104Bh)
00FA18AD add esp,8
printf("%d\n", c2 >> 3);//右移
00FA18B0 movsx eax,byte ptr [c2]
00FA18B4 sar eax,3
00FA18B7 push eax
00FA18B8 push offset string "%d\n" (0FA7B30h)
00FA18BD call _printf (0FA104Bh)
00FA18C2 add esp,8
上述反汇编代码中调用printf函数使用push/call指令,movsx语句中使用byte ptr指定数据类型,其余反汇编代码与汇编代码相同,不再赘述。
练习
1、
int a = -1;
int b = a > 3;
求变量b最后的值
int c = 0;
int d = !c;
求变量d最后的值
2、请写出下出对应的C语言代码。
00401010 push ebp
00401011 mov ebp,esp
00401013 sub esp,48h
00401016 push ebx
00401017 push esi
00401018 push edi
00401019 lea edi,[ebp-48h]
0040101C mov ecx,12h
00401021 mov eax,0CCCCCCCCh
00401026 rep stos dword ptr [edi]
00401028 mov dword ptr [ebp-4],0
0040102F xor eax,eax
00401031 cmp dword ptr [ebp-4],0
00401035 sete al
00401038 mov ecx,dword ptr [ebp-4]
0040103B add ecx,eax
0040103D mov dword ptr [ebp-8],ecx
00401040 push offset string "pause" (0042201c)
00401045 call system (00401090)
0040104A add esp,4
0040104D xor eax,eax
3、
int a = 7;
int b = ( a + 4 >4 ? 12 : 13);
本文摘自编程达人系列教材《汇编的角度——C语言》。