從C語言到彙編(二)算術運算

變量,常量,運算符可以構成表達式,表達式也可以構成語句。
本文將詳細介紹加(+),減(-),乘(*),除(/),取餘(%)運算符。

#include <stdio.h>
int main(void)
{
	int a=10;
	int b=20;
	int c;
	c=a+b;
	printf("sum=%d\n",c);
	
	return 0;
}
sum=30

c語言代碼計算了a+b的值,而a=10,b=20,這很容易計算,憑藉我們的大腦,就可以計算出結果。
彙編代碼

.section .rodata
	.LC0:.string "sum=%d\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $10,-4(%ebp);a
	movl $20,-8(%ebp);b
	movl -4(%ebp),%eax
	addl -8(%ebp),%eax
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

.section .rodata定義只讀存儲區數據。"sum=%d\n"就是隻讀字符串,一般跟代碼定義在一起。該字符串用於printf函數的輸出。
代碼定義了int a,b,c,佔用了12字節的空間,所以subl $12,%esp在棧中申請了12字節的空間。-4(%ebp)及緊跟着的4個字節代表了變量a,同理-8(%ebp)代表了b,-12(%ebp)代表了c。
movl $10,-4(%ebp)將10賦值給a,movl $20,-8(%ebp)將20賦值給了b。movl -4(%ebp),%eax將a賦值給%eax,addl -8(%ebp),%eax將b與%eax相加,並將結果保存給%eax.。相當於將a+b的值賦值給%eax,
movl %eax,-12(%ebp)將%eax的值賦值給-12(%ebp),相當於將a+b的值賦值給c。
pushl -12(%ebp)將c的值放入棧中。pushl $.LC0將.LC0的地址放入棧中。call printf調用printf函數輸出結果。addl $8,%esp清理printf參數所用空間。
用到的指令有

指令 效果 描述
pushl S R[%esp]<-R[%esp]-4 M[R[%esp]]<-S 將雙字壓入棧
popl D D<-M[R[%esp]] R[%esp]<-R[%esp]+4 將雙字出棧
call label 過程調用
add S,D D=D+S

S可以是立即數,寄存器,存儲器地址。D可以使寄存器,存儲器地址。但S和D不能同時爲存儲器地址。

#include <stdio.h>
int main(void)
{
	int a=1234;
	int b=136;
	int c=a+b;
	printf("sum=%d\n",c);
	
	c=a+121;
	printf("sum=%d\n",c);
	
	return 0;
}
sum=1370
sum=1355

彙編代碼

.section .rodata
	.LC0:.string "sum=%d\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $1234,-4(%ebp);a
	movl $136,-8(%ebp);b
	movl -4(%ebp),%eax
	addl -8(%ebp),%eax
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl -4(%ebp),%eax
	addl $121,%eax
	movl %eax,-12(%ebp)
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

addl $121,%eax將%eax的值與121相加,將結果賦值給%eax。

#include <stdio.h>
int main(void)
{
	int a=25;
	int b=100;
	int c=a-b;
	printf("value=%d\n",c);
	
	c=a-50;
	printf("value=%d\n",c);
}
value=-75
value=-25;

彙編代碼

.section .rodata
	.LC0:.string "value=%d\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $25,-4(%ebp);a
	movl $100,-8(%ebp);b
	movl -4(%ebp),%eax
	subl -8(%ebp),%eax
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl -4(%ebp),%eax
	subl $50,%eax
	movl %eax,-12(%ebp)
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

指令有

指令 效果 描述
SUB S,D D=D-S

subl -8(%ebp),%eax將%eax的值減去-8(%ebp)的值,相當於減去b。
subl $50,%eax 將%eax的值減去50。

#include <stdio.h>
int main(void)
{
	int a=12;
	int b=65;
	int c=a*b;
	printf("value=%d\n",c);
	
	c=a*50;
	printf("value=%d\n",c);
}

彙編代碼

.section .rodata
	.LC0:.string "value=%d\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $12,-4(%ebp);a
	movl $65,-8(%ebp);b
	movl -4(%ebp),%eax
	imull -8(%ebp),%eax
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl -4(%ebp),%eax
	imull $50,%eax
	movl %eax,-12(%ebp)
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

指令有

指令 效果 描述
IMUL S,D D=D*S

imull -8(%ebp),%eax 將%eax的值乘以-8(%ebp)的值,相當於乘以b。
imull $50,%eax 將%eax的值乘以50。

以上指令可同時用於無符號整數和有符號整數。

除/取餘

#include <stdio.h>
int main(void)
{
	int a=11;
	int b=5;
	int c=a/b;
	printf("value=%d\n",c);
	
	c=a%b;
	printf("value=%d\n",c);
}

彙編代碼

.section .rodata
	.LC0:.string "value=%d\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $12,-4(%ebp);a
	movl $65,-8(%ebp);b
	movl -4(%ebp),%eax
	cltd
	idivl -8(%ebp)
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl -4(%ebp),%eax
	cltd
	idivl -8(%ebp)
	movl %edx,-12(%ebp)
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

指令有

指令 效果 描述
cltd R[%edx]:R[%eax]<-SignExtend(R[%eax]) 轉爲四字
idivl S R[%edx]<-R[%edx]:R[%eax] mod S 有符號除法
R[%eax]<-R[%edx]:R[%eax] / S

cltd指令將%eax符號擴展到%edx中。idivl -8(%ebp)等於除以b。%eax中保存商,%edx中保存餘數。不過需要記住的是idivl不能除以立即數。那像a/10這樣的算術式編譯器是怎麼計算的呢?編譯器將除法轉化爲一系列乘法和右移指令。

無符號數除法/取餘

#include <stdio.h>
int main(void)
{
	unsigned int a=11;
	unsigned int b=5;
	unsigned int c=a/b;
	printf("value=%u\n",c);
	
	c=a%b;
	printf("value=%u\n",c);
}

彙編代碼

.section .rodata
	.LC0:.string "value=%u\n"
.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $12,-4(%ebp);a
	movl $65,-8(%ebp);b
	movl -4(%ebp),%eax
	movl $0,%edx
	divl -8(%ebp)
	movl %eax,-12(%ebp);c
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl -4(%ebp),%eax
	movl $0,%edx
	divl -8(%ebp)
	movl %edx,-12(%ebp)
	
	pushl -12(%ebp)
	pushl $.LC0
	call printf
	addl $8,%esp
	
	movl $0,%eax
	leave
	ret

可以清楚看到cltd被movl $0,%edx代替了。這樣保證了無符號數的擴展。
idivl -8(%ebp)被divl -8(%ebp)替代。

指令 效果 描述
divl S R[%edx]<-R[%edx]:R[%eax] mod S 無符號除法
R[%eax]<-R[%edx]:R[%eax] / S

再看一些複雜的表達式

#include <stdio.h>
int main(void)
{
	int a=5;
	int b=6;
	
	int c=a*a+b*b+2*a*b;
	
	return 0;
}

彙編代碼

.text
.global main

main:
	pushl %ebp
	movl %esp,%ebp
	
	subl $12,%esp
	movl $5,-4(%ebp);a
	movl $6,-8(%ebp);b
	movl -4(%ebp),%eax
	imull -4(%ebp),%eax
	movl -8(%ebp),%edx
	imull -8(%ebp),%edx
	addl %edx,%eax
	movl $2,%edx
	imull -4(%ebp),%edx
	imull -8(%ebp),%edx
	addl %edx,%eax
	movl %eax.-12(%ebp)
	
	movl $0,%eax
	leave
	ret	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章