程序的指令级表示(汇编)
预备知识1:
堆栈:先进后出,操作有push(),pop(),peek()
应用:函数调用 树的遍历(所有递归操作) 表达式的计算
预备知识2:
寄存器:CPU中临时存储数据的空间 小 快 贵
32位(0~31)EAX 以前8086是16位(0~15)AH,AL
通用寄存器 AX,BX,CX,DX:进行运算处理等 *函数调用的规则
寄存器ESI和EDI source和destination:copy数据
***EBP:描述了一个栈帧(一个栈内元素),永远指向一个栈帧的开始处 指向当前栈帧
***ESP:整个函数堆栈的栈顶,永远指向一个栈的栈顶
预备知识3:
汇编:GCC编译器(参见《深入理解计算机》)
MOVL $0x3051,%EAX 把0x3051这个值放到EAX寄存器,4个字节
MOVL %EAX,-12(%EBP) 把寄存器EAX的值,存放到EBP指向的地址减去12个字节的地方
LEAL -12(%EBP) %EAX 把%EBP减去12得到的地址,放到EAX寄存器当中
PUSHL %EBP 把寄存器EBP的值压栈
POPL %EBP 把栈顶的值弹出,存放到EBP寄存器中
SUBL %4 %ESP 把ESP寄存器的值减去4
预备知识4:
指针:内存地址 (参见《C与指针》)
指针的指针
类比java的引用
预备知识5:
C语言
(1)内存两种分配方式:
·静态分配:栈 编译器完成(编译时) 栈从上往下生长
·动态分配:堆 编译时无法确定运行时 堆从下往上生长
(2)函数帧
每个函数有个栈帧,栈帧大小不确定,因为要记录函数内部的信息,根据信息多少而定。
举例 函数A调用B,B调用C,C调用D,则栈中A到D地址逐渐减小(虚拟地址就是这样设计栈的)可能溢出
栈在内存中,EBP和ESP在寄存器(CPU中)
*EBP和ESP的工作原理
调用push操作,ESP值自动减4(减去4个字节)
调用pop操作,ESP值自动加4(减去4个字节)
(一定要注意栈从上往下生长,所以一个栈帧的开始处在高地址)
因为EBP和ESP只有一个,所以对他们的push和pop操作更多,用来记住其值。
机器级没有变量名!没有变量名的存储了!
例子:
int demo(){
int x=10;
int y=20;
int sum=add(&x,&y);
printf(“the sum is %d\n”,sum);
return sum;
}
int add(*xp,*yp)
{
int x =*xp;
int y=*yp;
return x+y;
}
转换成汇编语言(Intel中的)(自己应该动手写一下,顺便把栈和指针标出来)
Demo
1 pushl %ebp
2 movl %esp %ebp
3 subl %24 esp
4 movl $10 -4(%ebp)
5 movl $20 -8(%ebp)
6 leal -8(%ebp) %eax
7 movl %eax 4(%esp)
8 leal -4(%ebp) %eax
9 movl %eax esp
10 call add
11 打印结果
add:
- pushl %ebp
- movl %esp %ebp
- pushl %ebx
- movl 8(%ebp) %edx
- movl 12(%ebp) %ecx
- movl (%edx) %ebx
- movl (%ecx) %eax
- add %ebx %eax
- popl %ebx
- popl %ebp
- ret
Ps:ret是return语句,当add将最后结果算出,add函数帧弹栈以后,暴露出的内存地址里写的是主函数留下的打印指令的地址。