【新书推荐】4.4 关系运算符

本节必须掌握的知识点:

  示例十五

   代码分析

   汇编解析

4.4.1 示例十五

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

4.4.2 代码分析

由示例十五的输出结果可知,在VS编译器中,真为1,假为0。printf函数输出的结果就是关系表达式的结果,用真和假来表示。这些关系表达式可以是大于、大于等于、小于、小于等于、等于和不等于。计算机是如何处理和判断这些关系表达式的呢?我们接下来使用汇编代码来解释。

4.4.3 汇编解析

汇编代码

;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语言》。

你可能感兴趣的:(《汇编的角度——C语言》,c++,汇编)