内存栈的分配和堆的分配 。printf函数的认识

xiaoxiao2021-02-28  22

我们拿一个简单的程序来看一看。 在底层汇编之下函数的调用和内存的关系。 #include<stdio.h> int add(int a,int b) {  int c=0;  c=a+b;  return c; } void main() {  int a=10;  int b=20;  int z=0;  z=add(a,b);  printf("%d",z)     3: { 008E13A0  push        ebp  008E13A1  mov         ebp,esp 008E13A3  sub         esp,0CCh 008E13A9  push        ebx  008E13AA  push        esi  008E13AB  push        edi  008E13AC  lea         edi,[ebp-0CCh] 008E13B2  mov         ecx,33h 008E13B7  mov         eax,0CCCCCCCCh 008E13BC  rep stos    dword ptr es:[edi]      4:  int c=0; 008E13BE  mov         dword ptr [c],0      5:  c=a+b; 008E13C5  mov         eax,dword ptr [a] 008E13C8  add         eax,dword ptr [b] 008E13CB  mov         dword ptr [c],eax      6:  return c; 008E13CE  mov         eax,dword ptr [c]      7: } 008E13D1  pop         edi  008E13D2  pop         esi  008E13D3  pop         ebx  008E13D4  mov         esp,ebp 008E13D6  pop         ebp  008E13D7  ret     这是add函数的一部分,我们看出,先是要保留ebp里面的内容,,然后把一会可能用到的esi,edi,ebx压入到栈里面,然后分配给次函数栈帧,大小就是十六进制的cch,对其进行调试信息,将所分配的空间全赋值为cc,在VC之下我们可能经常看到的是“烫烫烫烫”,这是其对应的中文汉字,但这不是绝对的,有的编译器会出现“凸凸凸”的情形。随后计算之后,结果就使用eax返回,     你可能会问,那结果多于eax的字长那? 如果多余eax的字长,系统会使eax和ebx两个寄存器来返回结果,在eax里面放低位的值,在ebx里面放高位的值。 然后我们就将原来的寄存器的内容回复并恢复esp和ebp的内容。 但是,在函数调用后要将其传入的参数也释放掉,这就用到了函数的调用惯例,所谓的函数的调用惯例就是,传入参数的顺序,是从左还是从右,释放时是调用函数释放,还是函数自己本身释放。 就比如你要和一个外国人交流,要么他说你的语言,要么你说他的语言。     在c语言里面的默认调用方式是cdecl,也就是参数从左向右,被调用者释放的一种惯例。而且其名字修饰是在函数前面加上下划线。(所谓名字修饰就是为了在链接的时候对调用惯例进行区分)。      另外还存在着stdcall,此方式的调用不同于上面的是函数自己释放,等等。      栈一般的增长方式是向下增长的。      那为什么要引进堆那?     因为栈里面的变量的生存期都受到了函数的影响,调用函数不能对一个已经“死亡”的变量进行应用。但又人会说那我定义个全局变量就行了么。但全局变量的灵活性太差。  于是引进了堆。    堆的实现机制,其实就是向运行库申请一块内存,然后自己去管理。运行库就像超市的“物品”一样,应用进程去“超市”采购,若有,就卖给他,没有,运行库就通过API向系统申请更大的内存。 现在大家清楚了 malloc其实就相当一个运行库,他包装了4个函数,有,创建,申请,撤销,摧毁。
转载请注明原文地址: https://www.6miu.com/read-1650102.html

最新回复(0)