我们知道源文件在经过编译链接之后生成了目标文件,在映射完成后就开始执行,接下就是我们要谈的运行时内存管理。
有这么一道曾是腾讯面试时的题:
void add( void )
{
}
int main()
{
}
那么最后打印的结果是多少那?似乎一眼看去你会回答是10,但很遗憾正确答案是20,你或许会疑惑为什么a的值会改变,因为程序里似乎没有一个代码是给a直接赋值或间接赋值,接下来我会简单的讲解为什么a的值会改变。
程序在运行时所有的局部变量和函数都在栈上开辟内存,其他请求内存的函数如malloc等在堆上开辟,一般来说对的空间比栈大得多,栈的接口为esp,ebp寄存器,esp指向栈顶,ebp指向栈尾。用户可以向栈里面压入数据(push指令)每压入一个数据esp向上移动,也可已取出一个数据(pop指令),但是栈必须遵守一个规则,那就是先进后出。
这里要提出:栈保存类一个函数调用所需要的维护信息或活动记录
注意:地址从上而下为低到高
我们来讲解一个简单的程序来说明运行时内存管理:
#include
int add( int x, int y )
{
}
int main()
{
}
我们按下f10按钮进行调试,查看它的反汇编代码:
int main()
{
008313F0
008313F1
008313F3
008313F9
008313FA
008313FB
008313FC
00831402
00831407
0083140C

int a = 10;
0083140E
00831415
0083141C
00831423
00831425
00831427
0083142C
0083142F
在上面的第四行代码是跳转指令(这是一个近址相对位移调用指令),调用add函数,并且把下条指令的地址值压入栈中,跳转后的指令为:
int add( int x, int y )
{
008313A0
008313A1
008313A3
008313A9
008313AA
008313AB
008313AC
008313B2
008313B7
008313BC
008313BE
008313C5
008313C8
008313CB
008313CE
}
上面的代码指令与main函数开辟空间时基本相同,不一样的是下面的返回指令
008313D1
008313D2
008313D3
008313D4
008313D6
008313D7
通过上面的解释我们就能理解开始的那道题了,通过对add函数中变量的地址位移可以取得main函数ebp的值,进而可以对main函数中变量的值进行间接赋值。
由于我所使用的是vc2010系统,ebp与变量的空间,变量与变量的空间会有间隔,所以用开始的那道题进行操作推理时得不出答案(vc2008及以前的系统是可以实现的),事实上vc2010的系统更加严格,它会因为一个非法引用直接报错。
本文探讨了C语言程序运行时的内存管理,包括栈和堆的分配,以及函数调用时的内存布局。通过分析示例代码和反汇编,解释了局部变量如何在栈上分配,以及如何通过地址位移间接修改其他变量的值,揭示了内存管理的底层原理。




3689

被折叠的 条评论
为什么被折叠?



