汇编语言, 即第二代计算机语言,用一些容易理解和记忆的字母,单词来代替一个特定的指令。是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。(摘自百度百科)
简单来说,汇编语言就是一种底层的、面向机器的程序设计语言。我们可以通过阅读这种语言来加深对计算机工作原理的的理解,对计算机内部操作有一个更加清晰的认知。
1、PUSH X;
将执行如下操作:
a.将X的值入系统堆栈;
b.对esp(栈顶指针)自动减X的长度,通常为4.
2、POP X;
将执行如下操作:
a.将系统堆栈栈顶元素的值赋值给X;
b.将esp(栈顶指针)自动加X所占用的长度,通常是4.
3、call XXX(函数名);
该函数名的本质是这个函数的入口地址
将执行如下操作:
a.将call指令的下一条指令的首地址入系统堆栈,esp减小;
b.以将要执行的函数表达的首地址为下一条指令地址,更改CPU的IP寄存器(指令指针寄存器,这个寄存器直接决定了当前程序到底执行哪里的代码);
4、ret;
返回到主调函数(父函数)
将执行如下操作:
a.取得栈顶元素的值;
b.并赋值给IP寄存器.
下面用一个简单的例子来阐明与C语言其对应的汇编代码在操作系统中是如何执行的:
1、 C语言主函数代码:
int main()
{
int mainLocalVar = 30;
int var2 = 17;
mainLocalVar = functionOne(mainLocalVar, var2);
return 0;
}
汇编语言代码(
汇编代码中LIne x表明对应的C语言代码的行数):
_mainLocalVar$ = -4 //定义了该变量的偏移量为-4
_var2$ = -8 //定义了该变量的偏移量为-8
_main PROC NEAR
; Line 17
push ebp //栈底指针的值入系统堆栈
mov ebp, esp //栈底指针和栈顶指针指向同一个地方
sub esp, 8 //栈顶指针的值减8
; Line 18
mov DWORD PTR _mainLocalVar$[ebp], 30 ; 0000001eH //把30赋值给栈底指针向下的0到4字节的位置
; Line 19
mov DWORD PTR _var2$[ebp], 17 ; 00000011H //把17赋值给栈底指针向下的第5到第8个字节
; Line 21
mov eax, DWORD PTR _var2$[ebp] //把var2变量的值放进eax寄存器
push eax //eax入栈
mov ecx, DWORD PTR _mainLocalVar$[ebp] //把mainLocalVar变量的值放进ecx寄存器
push ecx //ecx入栈
call _functionOne //先把下一条语句的首地址放入系统堆栈,再执行functionOne函数
add esp, 8 //栈顶元素的值加8
mov DWORD PTR _mainLocalVar$[ebp], eax //eax的值赋值给mainLocalVar
; Line 23
xor eax, eax //对两个值进行或异运算
; Line 24
mov esp, ebp //ebp esp指向同一位置
pop ebp //把栈顶元素的值赋值给ebp
ret 0 //返回父函数
_main ENDP
2、functionOne函数C语言代码:
int functionOne(int formalVarInt, int formalVArShort) {
static int staticVarInt = 20;
staticVarInt += formalVarInt + formalVArShort;
++globolVar;
return globolVar + staticVarInt;
}
functionOne函数汇编代码:
_formalVarInt$ = 8 //定义formalVarInt的偏移量为8
_formalVArShort$ = 12 //定义formalVarShort的偏移量为12
_functionOne PROC NEAR
; File aboutSysStack.c
; Line 7
push ebp //栈底指针ebp的值入栈
mov ebp, esp //ebp和esp指向同一位置
; Line 10
mov eax, DWORD PTR _formalVarInt$[ebp] //formalVarInt的值放入eax寄存器
add eax, DWORD PTR _formalVArShort$[ebp] //formalVarShort的值与寄存器eax的值相加
mov ecx, DWORD PTR _?staticVarInt@?1??functionOne@@9@9 //staticVarInt的值放入寄存器ecx
add ecx, eax //eax和ecx的值相加
mov DWORD PTR _?staticVarInt@?1??functionOne@@9@9, ecx //ecx的值赋值给staticVarInt
; Line 11
mov edx, DWORD PTR _globolVar //变量globalVar入栈
add edx, 1 //edx的值加1
mov DWORD PTR _globolVar, edx //将edx的值赋值给globalVar
; Line 13
mov eax, DWORD PTR _globolVar //globalVar的值入eax寄存器
add eax, DWORD PTR _?staticVarInt@?1??functionOne@@9@9 //staticVarInt的值与eax的值相加
; Line 14
pop ebp //将栈顶元素的值赋值给ebp
ret 0 //返回到主调函数
_functionOne ENDP
下面给出图解:
从以上的C语言源代码与汇编语言代码的比较,可以得到如下结论:
1、全局变量与静态存储类变量的空间,是exe文件就制定好的空间,即数据段空间。
2、局部变量和形参变量占用的是系统堆栈空间。
3、形参与实参的关系:
a.实参表达式的值,将被"入栈"到系统堆栈空间;
b. 形参变量所占用的空间,其实就是对应的实参的值在系统的空间,这就是“值传递”的过程。
4、函数调用是存在系统消耗的。事实上,对于一个无参且无局部变量的函数的调用,至少需要消耗8B的系统堆栈空间。而系 统堆栈空间是极其有限的,因此,无度的递归调用,会使系统堆栈空间迅速消耗殆尽,并使得系统崩溃。
以上文章中若有讲解不周或有误的地方,欢迎各位大佬指正。