现在的位置: 首页 > 综合 > 正文

程序员的自我修养: 函数调用与堆栈

2012年08月15日 ⁄ 综合 ⁄ 共 3038字 ⁄ 字号 评论关闭

内存布局

linux下一个进程的典型内存分布:

 

函数与栈

栈保存一个函数调用所需要的维护信息,这常常被称为堆栈帧 或 活动记录.

保存如下内容:

1. 函数的放回地址和参数

2. 临时变量: 包括函数的非静态局部变量 以及 比一起自动生成的其他临时变量.

3. 保存的上下文: 包括在函数调用前后需要保持不变的寄存器.

一个常见的活动记录如下:

 

i386函数体的"标准"开头是这样的:

1. push ebp: 把ebp压入栈中

2. mov ebp, esp: ebp = esp (这时ebp指向栈顶, 而此时栈顶就是old ebp)

3. [可选]sub esp, XXX: 在栈上分配XXX字节的临时空间

4. [可选]push XXX: 如有必要,保存名为XXX的几个寄存器

 

那么"标准"结尾是这样的:

1. [可选]pop XXX: 如有必要, 恢复寄存器

2. mov esp, ebp: 恢复esp同时回收局部变量空间

3. pop: 从栈中恢复保存的ebp的值.

4. ret: 从栈中取得返回地址, 并跳转到该位置

 

反汇编一个函数:

int foo()

{

    return 123;

}

如下:

补充:

函数的调用:

call 0x0EA23h

将ECS或EIP压栈;

转移到0x0EA23h。

 

ret函数的返回:

ret

弹栈得到EIP或ECS;

返回EIP所指的指令继续执行。

 

函数返回值传递方式

用伪代码表示如下:

 

步骤如下:

1. 首先main函数在栈上额外开辟了一片空间, 并将这块空间的一部分作为传递返回值的临时对象,temp.

2. 将temp对象的地址作为隐藏参数传递给return_test函数

3. return_test函数将数据拷贝给temp对象, 并将temp对象的地址用eax传出.

4. return_test返回之后, main函数将eax指向的temp对象的内容拷贝给n.

 

当c++对象返回时,

 

结果:

 

当将函数改成这样时, C++进行了返回值优化, 直接将对象构造在传出时使用的临时对象上, 结果如下:

 

 

在windows中,利用VirtualAlloc取得的虚拟空间,类似于向操作系统"批发"空间.

而用HeapAlloc或者是malloc分配的空间实际上是堆空间的"零售".

这是因为如果内存管理由操作系统内核来进行,系统调用时的性能开销较大.而如果由程序自己管理时,有更高的效率.

堆分配算法有:

1. 空闲链表

2. 头/主体/空闲 位图

3. 对象池

在实际系统中, 是利用多种方法共同分配堆空间的.

抱歉!评论已关闭.