本节必须掌握的知识点:
示例十五
代码分析
汇编解析
C语言中的关系运算符如下表所示:
运算符 |
名称 |
示例 |
功能 |
< |
小于 |
i |
i小于j时返回真;否则返回假 |
> |
大于 |
i>j |
i大于j时返回真;否则返回假 |
<= |
小于等于 |
i<=j |
i小于等于j时返回真;否则返回假 |
>= |
大于等于 |
i>=j |
i大于等于j时返回真;否则返回假 |
== |
等于 |
i==j |
i等于j时返回真;否则返回假 |
!= |
不等于 |
i!=j |
i不等于j时返回真;否则返回假 |
表4-4 关系运算符
示例代码十五
/*
关系运算符
*/
#include
#include
int main(void) {
int i = 5, j = 6;
printf("%d\n", i > j);
printf("%d\n", i < j);
printf("%d\n", i <= j);
printf("%d\n", i >= j);
printf("%d\n", i == j);
printf("%d\n", i != j);
system("pause");
return 0;
}
●输出结果:
0
1
1
0
0
1
由示例十五的输出结果可知,在VS编译器中,真为1,假为0。printf函数输出的结果就是关系表达式的结果,用真和假来表示。这些关系表达式可以是大于、大于等于、小于、小于等于、等于和不等于。计算机是如何处理和判断这些关系表达式的呢?我们接下来使用汇编代码来解释。
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data
i sdword ?
j sdword ?
.const
szMsg db "%d",0dh,0ah,0
.code
start:
mov sdword ptr i,5
mov sdword ptr j,6
;i>j
mov eax,i
cmp eax,j ;比较变量i和j的大小
jg G1 ;如果i>j则跳转G1地址处
mov eax,0 ;否则eax=0
jmp G2 ;输出结果
G1:
mov eax,1
G2:
invoke printf,offset szMsg,eax;输出结果
;i mov eax,i cmp eax,j ;比较变量i和j的大小 jl L1 ;如果i mov eax,0 ;否则eax=0 jmp L2 ;输出结果 L1: mov eax,1 L2: invoke printf,offset szMsg,eax;输出结果 ;i<=j mov eax,i cmp eax,j ;比较变量i和j的大小 jle LE1 ;如果i<=j则跳转LE1地址处 mov eax,0 ;否则eax=0 jmp LE2 ;输出结果 LE1: mov eax,1 LE2: invoke printf,offset szMsg,eax;输出结果 ;i>=j mov eax,i cmp eax,j ;比较变量i和j的大小 jge GE1 ;如果i>=j则跳转GE1地址处 mov eax,0 ;否则eax=0 jmp GE2 ;输出结果 GE1: mov eax,1 GE2: invoke printf,offset szMsg,eax;输出结果 ;i==j mov eax,i cmp eax,j ;比较变量i和j的大小 jz EQ1 ;如果i==j则跳转EQ1地址处 mov eax,0 ;否则eax=0 jmp EQ2 ;输出结果 EQ1: mov eax,1 EQ2: invoke printf,offset szMsg,eax;输出结果 ;i!=j mov eax,i cmp eax,j ;比较变量i和j的大小 jnz NE1 ;如果i!=j则跳转NE1地址处 mov eax,0 ;否则eax=0 jmp NE2 ;输出结果 NE1: mov eax,1 NE2: invoke printf,offset szMsg,eax;输出结果 ; invoke _getch ret end start ●输出结果: 0 1 1 0 0 1 由上述汇编代码我们得知,C语言的关系表达式大大简化了汇编代码的JCC条件判断语句。 C语言关系表达式:i>j。 对应的汇编JCC条件判断语句: mov eax,i cmp eax,j ;比较变量i和j的大小 jg G1 ;如果i>j则跳转G1地址处 mov eax,0 ;否则eax=0 jmp G2 ;输出结果 G1: mov eax,1 G2: invoke printf,offset szMsg,eax;输出结果 事实是C语言编译器自动将C语言的关系表达式翻译为汇编的JCC条件判断语句。 cmp指令为不保存运算结果的减法指令,只影响标志位。 由于变量i和变量j为int类型的有符号整数,对应的汇编数据类型为sdword,因此只能使用有符号数JCC指令:jg(大于)、jge(大于等于)、jl(小于)、jle(小于等于)、jz(等于)、jnz(不等于)。 关系表达式的结果保存在eax寄存器中,作为printf函数的参数输出。 ■反汇编代码 int i = 5, j = 6; 00BC1838 mov dword ptr [i],5 int i = 5, j = 6; 00BC183F mov dword ptr [j],6 printf("%d\n", i > j); 00BC1846 mov eax,dword ptr [i] 00BC1849 cmp eax,dword ptr [j] 00BC184C jle main+4Ah (0BC185Ah) ;如果i <= j则跳转到0BC185Ah地址处 00BC184E mov dword ptr [ebp-0DCh],1 ;否则[ebp-0DCh]=1 00BC1858 jmp main+54h (0BC1864h) 00BC185A mov dword ptr [ebp-0DCh],0 ;[ebp-0DCh]=0 00BC1864 mov ecx,dword ptr [ebp-0DCh] 00BC186A push ecx ;ecx=0 00BC186B push offset string "%d\n" (0BC7B30h) 00BC1870 call _printf (0BC104Bh) 00BC1875 add esp,8 printf("%d\n", i < j); 00BC1878 mov eax,dword ptr [i] 00BC187B cmp eax,dword ptr [j] 00BC187E jge main+7Ch (0BC188Ch) 00BC1880 mov dword ptr [ebp-0DCh],1 00BC188A jmp main+86h (0BC1896h) 00BC188C mov dword ptr [ebp-0DCh],0 00BC1896 mov ecx,dword ptr [ebp-0DCh] 00BC189C push ecx 00BC189D push offset string "%d\n" (0BC7B30h) 00BC18A2 call _printf (0BC104Bh) 00BC18A7 add esp,8 printf("%d\n", i <= j); 00BC18AA mov eax,dword ptr [i] 00BC18AD cmp eax,dword ptr [j] 00BC18B0 jg main+0AEh (0BC18BEh) 00BC18B2 mov dword ptr [ebp-0DCh],1 00BC18BC jmp main+0B8h (0BC18C8h) 00BC18BE mov dword ptr [ebp-0DCh],0 00BC18C8 mov ecx,dword ptr [ebp-0DCh] 00BC18CE push ecx 00BC18CF push offset string "%d\n" (0BC7B30h) 00BC18D4 call _printf (0BC104Bh) 00BC18D9 add esp,8 printf("%d\n", i >= j); 00BC18DC mov eax,dword ptr [i] 00BC18DF cmp eax,dword ptr [j] 00BC18E2 jl main+0E0h (0BC18F0h) 00BC18E4 mov dword ptr [ebp-0DCh],1 00BC18EE jmp main+0EAh (0BC18FAh) 00BC18F0 mov dword ptr [ebp-0DCh],0 00BC18FA mov ecx,dword ptr [ebp-0DCh] 00BC1900 push ecx 00BC1901 push offset string "%d\n" (0BC7B30h) 00BC1906 call _printf (0BC104Bh) 00BC190B add esp,8 printf("%d\n", i == j); 00BC190E mov eax,dword ptr [i] 00BC1911 cmp eax,dword ptr [j] 00BC1914 jne main+112h (0BC1922h) 00BC1916 mov dword ptr [ebp-0DCh],1 00BC1920 jmp main+11Ch (0BC192Ch) 00BC1922 mov dword ptr [ebp-0DCh],0 00BC192C mov ecx,dword ptr [ebp-0DCh] 00BC1932 push ecx 00BC1933 push offset string "%d\n" (0BC7B30h) 00BC1938 call _printf (0BC104Bh) 00BC193D add esp,8 printf("%d\n", i != j); 00BC1940 mov eax,dword ptr [i] 00BC1943 cmp eax,dword ptr [j] 00BC1946 je main+144h (0BC1954h) printf("%d\n", i != j); 00BC1948 mov dword ptr [ebp-0DCh],1 00BC1952 jmp main+14Eh (0BC195Eh) 00BC1954 mov dword ptr [ebp-0DCh],0 00BC195E mov ecx,dword ptr [ebp-0DCh] 00BC1964 push ecx 00BC1965 push offset string "%d\n" (0BC7B30h) 00BC196A call _printf (0BC104Bh) 00BC196F add esp,8 我们来分析反汇编代码中的这一段代码: printf("%d\n", i > j); 00BC1846 mov eax,dword ptr [i] 00BC1849 cmp eax,dword ptr [j] 00BC184C jle main+4Ah (0BC185Ah) ;如果i <= j则跳转到0BC185Ah地址处 00BC184E mov dword ptr [ebp-0DCh],1 ;否则[ebp-0DCh]=1 00BC1858 jmp main+54h (0BC1864h) 00BC185A mov dword ptr [ebp-0DCh],0 ;[ebp-0DCh]=0 00BC1864 mov ecx,dword ptr [ebp-0DCh] 00BC186A push ecx ;ecx=0 00BC186B push offset string "%d\n" (0BC7B30h) 00BC1870 call _printf (0BC104Bh) 反汇编代码中将i > j关系表达式翻译成汇编时,使用了jle指令,而汇编代码中使用的是jg指令,其实二者的逻辑是相同的,并不影响结果的判断。剩下的几个表达式的反汇编代码与此相似,此处不再赘述。 【注意】JCC指令中,JZ等价于JE,JNE等价于JNZ。更多JCC指令相关的内容请查阅《X86汇编基础教程》第八章8086指令系统。 练习 1、读取两个整数,显示它们的商和余数 2、读取两个整数,然后显示出前者是后者的百分之几。 3、读取两个整数,然后输出他们的和以及乘积。 4、显示读取整数的最后一位数字。 5、读取三个整数,并显示出它们的合计值和平均值。 6、读取两个整数并用浮点数显示出它们平均值(类型转换)。 7、编写程序对整型常量、浮点常量、int型变量和double型变量进行乘除等各种运算。 本书摘自编程达人系列教材《汇编的角度——C语言》。