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