本章讲解逻辑运算符与逻辑运算,移位运算符与移位运算。
本章学习知识概要:
逻辑运算符
位运算符
本节必须掌握的知识点:
示例十六
代码分析
汇编解析
逻辑运算符包括逻辑与、逻辑或、逻辑非,如表5-1所示。
运算符 |
表达式 |
说明 |
&&逻辑与(并且) |
条件1&&条件2 |
两个条件同时为真时,结果为真 |
||逻辑或(或) |
条件1||条件2 |
两个条件有一个为真时,结果为真 |
!逻辑非(非) |
!条件 |
条件为真时,结果为假 条件为假时,结果为真 |
表5-1逻辑运算符
■&&逻辑与
语法格式:expr1 && expr2;
当两个条件同时为真时,结果为真;否则为假。(在C语言中,非0为真,0为假)。
举例
1 && 2 //真&&真 -->结果为真
0 && 1 //假&&真 -->结果为假;当expr1为假时,将直接返回假,不会再计算expr2。
-1 && 0 //真&&假 -->结果为假
2>3 && 2 //假&&真 -->结果为假;当expr1为假时,将直接返回假,不会再计算expr2。
■||逻辑或
语法格式:expr1 || expr2;
当两个条件有一个为真时,结果为真;否则为假。
举例
0 || 1 //假||真 -->结果为真;当expr1为假时,会继续计算expr2。
0 || 0 //假||假 -->结果为假;当expr1为假时,会继续计算expr2,expr2为假,则为假(两个条件都为假时,结果为假)。
■!逻辑非
语法格式:!expr;当条件为真时,结果为假;当条件为假时,结果为真。
举例
!0 //假 -->结果为真
!1 //真 -->结果为假
!(1>2) //假 -->结果为真
■德摩根定律
对各条件取非,然后将逻辑与变为逻辑或,逻辑或变为逻辑与,然后再取其否定,结果和原条件一样。
举例
x && y 和 !(! x || !y )相等
x || y 和 !(! x && !y )相等
【注意】单目运算符 ‘-’取补码,单目运算符‘~’ 符号取反。
示例代码十六
/*
逻辑运算符
*/
#include
#include
int main(void) {
int x = 2,y = 4,z = 6;
int num;
printf("请输入一个整数:");
scanf_s("%d", &num);
if (y < num && z > num)
printf("两个条件表达式同时为真时,逻辑与表达式为真!\n");
if (x == num || y == num)
printf("其中任意一个表达式为真时,逻辑或表达式为真!\n");
if (!num)
printf("num为零时,逻辑非表达式为真!\n");
printf("num符号取反之后的值是%d\n", -num); //取num的补码
printf("num补码的值是%d\n", ~num); //num符号取反
system("pause");
return 0;
}
输出结果:
测试1
请输入一个整数:5
两个条件表达式同时为真时,逻辑与表达式为真!
num补码的值是-5
num符号取反之后的值是-6
测试2
其中任意一个表达式为真时,逻辑或表达式为真!
num补码的值是-4
num符号取反之后的值是-5
测试3
请输入一个整数:0
num为零时,逻辑非表达式为真!
num补码的值是0
num符号取反之后的值是-1
示例十六代码中定义了3个变量x,y,z。然后有用户输入一个整数值存入变量num中。接下来3个if条件语句中各包含一个逻辑运算符&&、||、!。如果if语句条件为真,使用printf函数输出结果。最后一个printf语句使用单目运算符 “-”符号取反,然后输出结果。
当我们需要同时判断多个条件表达式时,我们可以使用“&&”逻辑与运算符或者“||”逻辑或运算符将多个条件表达式连接起来。
如果使用“&&”逻辑与运算符,连接的两个条件表达式同时为真时,结果为真;如果其中一个条件表达式为假,则结果为假。可以尽量选择条件为假可能性较大的表达式前置,如果前置条件表达式为假,则结果为假,后置的条件表达式就无需判断了,这样可以提高程序的运行效率。
如果使用“||”逻辑或运算符,连接的两个条件表达式只要其中一个为真,则结果为真;如果两个表达式全部为假,则结果为假。我们可以选择条件为真可能性较大的表达式前置,当前置表示为真时,结果为真,后置的表达式就无需判断了。
符号取反单目运算符 ‘~’。取补码运算符‘-’并不是逻辑运算符。
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
x sdword 2
y sdword 4
z sdword 6
num sdword ?
.const
szMsg1 db "请输入一个整数:",0
szMsg2 db "%d",0
szMsg3 db "两个条件表达式同时为真时,逻辑与表达式为真!",0dh,0ah,0
szMsg4 db "其中任意一个表达式为真时,逻辑或表达式为真!",0dh,0ah,0
szMsg5 db "num为零时,逻辑非表达式为真!",0dh,0ah,0
szMsg6 db "num符号取反之后的值是%d",0dh,0ah,0
szMsg7 db "num补码的值是%d",0dh,0ah,0
.code
start:
;输入整数num
invoke printf,offset szMsg1
invoke scanf,offset szMsg2,ADDR num
;
mov eax,num
.if (y < eax) && (z > eax)
invoke printf,offset szMsg3
.endif
mov eax,num
.if (x == eax) || (y == eax)
invoke printf,offset szMsg4
.endif
.if !num
invoke printf,offset szMsg5
.endif
;补码
mov eax,num
neg eax
invoke printf,offset szMsg7,eax;输出结果
;符号位取反
mov eax,num
not eax ;not取反指令,各位取反,符号位不变
mov num,eax
invoke printf,offset szMsg6,num;输出结果
;
invoke _getch
ret
end start
输出结果与C代码相同。
上述汇编代码使用了.if\.else\.endif高级汇编决策伪指令,与C语言中的if/else语句基本相同。C语言中的‘-’运算符对应汇编中的neg求补指令。C语言中的‘~’运算符对应汇编not取反指令。
■反汇编代码
int x = 2,y = 4,z = 6;
00EF4ED2 mov dword ptr [x],2
int x = 2,y = 4,z = 6;
00EF4ED9 mov dword ptr [y],4
00EF4EE0 mov dword ptr [z],6
int num;
printf("请输入一个整数:");
00EF4EE7 push offset string "\xc7\xeb\xca\xe4\xc8\xeb\xd2\xbb\xb8\xf6\xd5\xfb\xca\xfd\xa3\xba" (0EF7B30h)
00EF4EEC call _printf (0EF104Bh)
00EF4EF1 add esp,4
scanf_s("%d", &num);
00EF4EF4 lea eax,[num]
00EF4EF7 push eax
00EF4EF8 push offset string "%d" (0EF7B48h)
00EF4EFD call _scanf_s (0EF138Eh)
00EF4F02 add esp,8
if (y < num && z > num)
00EF4F05 mov eax,dword ptr [y]
00EF4F08 cmp eax,dword ptr [num]
00EF4F0B jge main+82h (0EF4F22h)
00EF4F0D mov eax,dword ptr [z]
00EF4F10 cmp eax,dword ptr [num]
00EF4F13 jle main+82h (0EF4F22h)
printf("两个条件表达式同时为真时,逻辑与表达式为真!\n");
00EF4F15 push offset string "\xc1\xbd\xb8\xf6\xcc\xf5\xbc\xfe\xb1\xed\xb4\xef\xca\xbd\xcd\xac\xca\xb1\xce\xaa\xd5\xe6\xca\xb1\xa3\xac\xc2\xdf\xbc\xad\xd3@"... (0EF7E40h)
00EF4F1A call _printf (0EF104Bh)
00EF4F1F add esp,4
if (x == num || y == num)
00EF4F22 mov eax,dword ptr [x]
00EF4F25 cmp eax,dword ptr [num]
00EF4F28 je main+92h (0EF4F32h)
00EF4F2A mov eax,dword ptr [y]
00EF4F2D cmp eax,dword ptr [num]
00EF4F30 jne main+9Fh (0EF4F3Fh)
printf("其中任意一个表达式为真时,逻辑或表达式为真!\n");
00EF4F32 push offset string "\xc6\xe4\xd6\xd0\xc8\xce\xd2\xe2\xd2\xbb\xb8\xf6\xb1\xed\xb4\xef\xca\xbd\xce\xaa\xd5\xe6\xca\xb1\xa3\xac\xc2\xdf\xbc\xad\xbb@"... (0EF85E8h)
00EF4F37 call _printf (0EF104Bh)
00EF4F3C add esp,4
if (!num)
00EF4F3F cmp dword ptr [num],0
if (!num)
00EF4F43 jne main+0B2h (0EF4F52h)
printf("num为零时,逻辑非表达式为真!\n");
00EF4F45 push offset string "num\xce\xaa\xc1\xe3\xca\xb1\xa3\xac\xc2\xdf\xbc\xad\xb7\xc7\xb1\xed\xb4\xef\xca\xbd\xce\xaa\xd5\xe6\xa3\xa1\n" (0EF7BECh)
00EF4F4A call _printf (0EF104Bh)
00EF4F4F add esp,4
printf("num补码的值是%d\n", -num);//取num的补码
00EF4F52 mov eax,dword ptr [num]
00EF4F55 neg eax
00EF4F57 push eax
00EF4F58 push offset string "num\xb2\xb9\xc2\xeb\xb5\xc4\xd6\xb5\xca\xc7%d\n" (0EF7FA4h)
00EF4F5D call _printf (0EF104Bh)
00EF4F62 add esp,8
printf("num符号取反之后的值是%d\n", ~num);//num符号取反
00EF4F65 mov eax,dword ptr [num]
00EF4F68 not eax
00EF4F6A push eax
00EF4F6B push offset string "num\xb7\xfb\xba\xc5\xc8\xa1\xb7\xb4\xd6\xae\xba\xf3\xb5\xc4\xd6\xb5\xca\xc7%d\n" (0EF7CF8h)
00EF4F70 call _printf (0EF104Bh)
00EF4F75 add esp,8
上述C语言的反汇编代码,我们可以清晰的看到,C语言中的条件表达式被翻译为CMP/JCC条件判断语句:
if (y < num && z > num)
00EF4F05 mov eax,dword ptr [y]
00EF4F08 cmp eax,dword ptr [num]
00EF4F0B jge main+82h (0EF4F22h) ;如果y>=num,则跳转到0EF4F22h地址处
00EF4F0D mov eax,dword ptr [z]
00EF4F10 cmp eax,dword ptr [num]
00EF4F13 jle main+82h (0EF4F22h) ;如果z<=num,则跳转到0EF4F22h地址处
…
00EF4F1A call _printf (0EF104Bh)
…
if (x == num || y == num)
00EF4F22 mov eax,dword ptr [x]
结论
1.C语言中的条件表达式最终被翻译为汇编语言中的JCC条件判断语句。
2.C语言语句和高级汇编的决策伪指令非常相似。
3.如果把汇编语言中的寄存器改为编译器自动选取,CMP/JCC条件判断语句简化为条件表达式,汇编语言就变成了C语言。
本文摘自编程达人系列教材《汇编的角度——C语言》。资料下载:www.bcdaren.com