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