if
從最簡單的if語句開始。
#include <stdio.h>
int main(void)
{
int a;
scanf("%d",&a);
if((a&1)==0)
{
printf("is even!");
}
return 0;
}
最簡單的if語句如上。我們將其轉化爲彙編。
.section .rodata
.LC0:.string "%d"
.LC1:.string "is even!"
.text
.global main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $.LC0
call scanf
addl $8,%esp
movl -4(%ebp),%eax
andl $1,%eax
cmpl $0,%eax
jne .L1
pushl $.LC1
call printf
addl $4,%esp
.L1:
movl $0,%eax
leave
ret
最新用到的指令。
指令 | 基於 | 描述 |
---|---|---|
cmpl S2,S1 | S1-S2 | 比較 |
注意這裏的S2是減數,S1是被減數,與我們使用的慣例是相反的。
cmpl 指令會將S1減去S2,但只設置條件碼,不改變參數的值。
條件碼
除了整數寄存器,CPU還維護着一組單個位的條件碼寄存器,它們描述了最近的算數運算或邏輯運算操作的屬性。常用的條件碼有:
CF:進位標誌。
ZF:零標誌。
SF:符號標誌。
OF:溢出標誌。
跳轉
指令 | 跳轉條件 | 描述 |
---|---|---|
jmp Label | 1 | 直接跳轉 |
jmp *Operand | 1 | 間接跳轉 |
je Label | ZF | 相等/零 |
jne Label | ~ZF | 不相等/非零 |
js Label | SF | 負數 |
jns Label | ~SF | 非負數 |
jg Label | (SF^OF)&ZF | 大於(有符號>) |
jge Label | ~(SF^OF) | 大於等於(有符號>=) |
jl Label | SF^OF | 小於(有符號<) |
jle Label | (SF^OF) | ZF |
ja Label | CF&ZF | 超過(無符號>) |
jae Label | ~CF | 超過或相等(無符號>=) |
jb Label | CF | 低於(無符號<) |
jbe Label | CF | ZF |
movl -4(%ebp),%eax 將a的值放入%eax中,andl $1,%eax 將1的值與%eax的值相與。cmpl $0,%eax比較%eax與0,jne .L1不相等則跳轉.L1處,如果相等則執行printf(“is even!”)。
這裏要說明,(a&1)==0 被翻譯爲不等於0則跳轉,否則繼續執行,這是慣例,因爲當if語句爲if-else if-else時,這樣寫才能便於翻譯。
以上條件是一條關係語句,他也可以是邏輯語句。
#include <stdio.h>
int main(void)
{
int a;
scanf("%d",&a);
if(a>=0&&a<=100)
{
printf("ok!");
}
return 0;
}
彙編代碼
.section .rodata
.LC0:.string "%d"
.LC1:.string "ok!"
.text
.global main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $.LC0
call scanf
addl $8,%esp
cmpl $0,-4(%ebp)
jl .L1
cmpl $100,-4(%ebp)
jg .L1
pushl $.LC1
call printf
addl $4,%esp
.L1:
movl $0,%eax
leave
ret
下面分析一下彙編代碼與c代碼的聯繫。
cmpl $0,-4(%ebp) jl .L1 如果a<0則跳出if語句。直接執行下面語句,這與邏輯與的短路相匹配。如果a>=0這繼續執行。
cmpl $100,-4(%ebp) jg .L1 如果a>100則跳出if語句。直接執行下面語句,如果a<=100這繼續執行printf(“ok!”)。
再看邏輯或。
#include <stdio.h>
int main(void)
{
int a;
scanf("%d",&a);
if(a<0||a>100)
{
printf("bad!");
}
return 0;
}
彙編代碼
.section .rodata
.LC0:.string "%d"
.LC1:.string "bad!"
.text
.global main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $.LC0
call scanf
addl $8,%esp
cmpl $0,-4(%ebp)
jl .L1
cmpl $100,-4(%ebp)
jle .L2
.L1
pushl $.LC1
call printf
addl $4,%esp
.L2:
movl $0,%eax
leave
ret
下面分析一下彙編代碼與c代碼的聯繫。
cmpl $0,-4(%ebp) jl .L1 如果a<0,則跳轉到if語句執行。否則繼續執行條件判斷。
cmpl $100,-4(%ebp) jg .L2 如果a<=100,這跳出if語句,否則執行if語句。
這裏也可以看出邏輯或的短路性質。
if-else
先看C語言代碼。
#include <stdio.h>
int main(void)
{
int a;
scanf("%d",&a);
if(a&1==0)
{
printf("even\n");
}else
{
printf("odd\n");
}
return 0;
}
看彙編代碼
.section .rodata
.LC0:.string "%d"
.LC1:.string "even\n"
.LC2:.string "odd\n"
.text
.global main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $.LC0
call scanf
addl $8,%esp
movl -4(%ebp),%eax
andl $1,%eax
cmpl $0,%eax
jne .L1
pushl $.LC1
call printf
addl $4,%esp
jmp .L2
.L1:
pushl $.LC2
call printf
addl $4,%esp
.L2:
movl $0,%eax
leave
ret
如果a&1!=0則執行.L1—執行printf(“odd\n”),
否則執行printf(“even\n”),執行完後,執行jmp跳轉到.LC2,離開if-else語句。
if-else if-else
先看代碼。
#include <stdio.h>
int main(void)
{
int a;
scanf("%d",&a);
if(a>100)
{
printf("bad\n");
}else if(a>=90)
{
printf("A\n");
}else if(a>=80)
{
printf("B\n");
}else
{
printf("C\n");
}
return 0;
}
彙編代碼
.section .rodata
.LC0:.string "%d"
.LC1:.string "bad\n"
.LC2:.string "A\n"
.LC3:.string "B\n"
.LC4:.string "C\n"
.text
.global main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $.LC0
call scanf
addl $8,%esp
cmpl $100,-4(%ebp)
jle .L1
pushl $.LC1
call printf
addl $4,%esp
jmp .L2
.L1:
cmpl $90,-4(%ebp)
jl .L3
pushl $.LC2
call printf
addl $4,%esp
jmp .L2
.L3:
cmpl $80,-4(%ebp)
jl .L4
pushl $.LC3
call printf
addl $4,%esp
jmp .L2
.L4:
pushl $.LC4
call printf
addl $4,%esp
.L2:
movl $0,%eax
leave
ret