首页 > 代码库 > 栈帧的组织——C/C++内存管理必须掌握的

栈帧的组织——C/C++内存管理必须掌握的

 

程序栈

      说到栈帧,不得不先说说程序栈。

      程序栈是支持函数执行的内存区域,通常和堆共享。程序栈通常占据内存区域的下部,而堆用的是上部。

      程序栈存放栈帧,栈帧有时候也称为活跃记录或活跃帧。栈帧存放函数参数和局部变量。堆存放动态内存。

      调用函数时,函数的栈帧被推到栈上,栈向上长出一个栈帧。当函数终止时,其栈帧从程序栈上弹出。栈帧所使用的内存不会被清理,但是最终可能会被推到程序栈上的另一个栈帧覆盖。

       动态分配的内存来自堆,堆向下生长。随着内存的分配与释放,堆中会布满碎片。尽管堆是向下生长的,但是这只是大体方向,实际内存可能位于堆上的任意位置。

 

栈帧的组织

    

 栈帧由下面几个元素组成:

    返回地址:函数完成后要返回的程序内部地址   局部数据存储:为局部变量分配的内存   参数存储:为函数参数分配的内存   栈指针和基指针:运行时系统用来管理栈的指针    

     栈指针通常指向栈顶部。基指针通常存在并指向栈帧内部的地址,比如返回地址,用来协调访问栈帧内部的元素。这两个指针都不是C指针,他们是运行时系统管理程序栈的地址。

    下面这个函数给出实例:

float average(int *arr, int size){     int sum;     printf("arr: %p\n",&arr);     printf("size: %p\n",&size);     priintf("sum: %p\n",&sum);     for(int i = 0;i < size;i ++){          sum +=arr[i];     }          return (sum *1.0f) / size;}//output//arr: 0x500//size: 0x504//sum: 0x480


  其中参数地址和局部变量之间的空档,保存的是运行时系统管理栈所需的其他栈帧元素。

  系统在创建栈帧时,将参数以跟声明时相反的顺序推到栈上,最后推入局部变量。在这个例子中,size在arr之后被推入。通常,接下来会推入函数调用的返回地址,然后是局部变量。推入它们的顺序和在代码中的顺序相反!

   从原理上说,本例中的栈向上生长。不过栈帧的参数和局部变量以及新栈帧被添加到了低内存地址。栈的实际生长方向和实际相关。

   for语句中的i没有包含在栈帧中。这是因为C语言中把块语句当成“微型”函数。

   将栈帧推到程序栈上时,系统可能会耗尽内存,这种情况叫做栈溢出,通常会导致程序非正常终止。要牢记每个线程都会有自己的程序栈,一个或多个线程访问内存中同一个对象可能会造成冲突!