从C语言到汇编(三)if语句

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
	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章