c语言栈帧

C语言栈帧:向下生长,高地址-->地地址(减法),帧指针ebp,每次被调用,保存原先的帧指针,ebp指向被调用栈帧的高地址;栈指针esp,指向栈顶,每次入栈,esp减,出栈,esp加

 

 

main()也是一个函数。

这是因为在编译连接时它将会作为crt0.s汇编程序的函数被调用。crt0.s是一个桩(stub)程序,名称中的“crt”是“C run-time”的缩写。该程序的目标文件将被链接在每个用户执行程序的开始部分,主要用于设置一些初始化全局变量。通常使用gcc编译链接生成文件时,gcc会自动把该文件的代码作为第一个模块链接在可执行程序中。在编译时使用显示详细信息选项“-v”就可以明显地看出这个链接操作过程。因此在通常的编译过程中,我们无需特别指定stub模块crt0.o。为了使用ELF格式的目标文件以及建立共享库模块文件,现在的编译器已经把crt0扩展成几个模块:crt1.0、crti.o、crtbegin.o、crtend.o和crtn.o。这些模块的链接顺序为crt1.o、crti.o、crtbegin.o(crtbeginS.o)、所有程序模块、crtend.o(crtendS.o)、crtn.o、库模块文件。gcc的配置文件specfile指定了这种链接顺序。其中,crt1.o、crti.o和crtn.o由C库提供,是C程序的“启动”模块;crtbegin.o和crtend.o是C++语言的启动模块,由编译器gcc提供;而crt1.o则与crt0.o的作用类似,主要用于在调用main()之前做一些初始化工作,全局符号_start就定义在这个模块中。crtbegin.o和crtend.o主要用于C++语言,在.ctors和.dtors区中执行全局构造(constructor)和析构(destructor)函数。crtbeginS.o和crtendS.o的作用与前两者类似,但用于创建共享模块中。crti.o用于在.init区中执行初始化函数init()。.init区中包含进程的初始化代码,即当程序开始执行时,系统会在调用main()之前先执行.init中的代码。crtn.o则用于在.fini区中执行进程终止退出处理函数fini()函数,即当程序正常退出时(main()返回之后),系统会安排执行.fini中的代码。

### C语言运行时动态工作原理 #### 函数调用过程中的创建 当一个函数被调用时,在内存中会为其分配一块称为的空间。这块空间用于保存该函数所需的各种信息,包括参数、局部变量以及返回地址等[^1]。 对于C语言而言,每当进入一个新的函数体之前,CPU会先将当前指令指针(EIP)指向下一个待执行语句的位置,并将其压入堆;接着把旧基址寄存器(EBP)的内容也推送到堆上,以便稍后恢复原来的环境设置。之后更新 EBP 的值使其指向新的顶位置,从而建立起新一层的作用域链表头节点——也就是所谓的“活动记录”或“桢”。 ```assembly push ebp ; 保护老的ebp mov ebp, esp ; 设置新的ebp为当前esp sub esp, N ; 为局部变量预留N字节空间 ``` 上述汇编代码片段展示了如何初始化一个典型的结构。这里 `N` 表示为局部变量所保留的空间大小,具体取决于实际需求。 #### 局部变量与参数布局 在内部分配给各个元素的具体偏移量遵循一定的规律: - 参数通常位于 `[ebp + offset]` 处; - 调用者传来的实参按照从右向左顺序依次排列于高地址端; - 返回地址则紧随其后的较低地址处 (`[ebp + 4]`); - 当前函数定义的形参会紧接着放置在其下方(`[ebp + 8], [ebp + 12]...`); - 自身声明的所有自动类型的局部对象占据更低地址区域内的连续区间(`[ebp - n]`); 这种安排使得通过相对固定的偏移来快速定位任意一项成为可能,同时也方便了调试工具解析程序状态。 #### 访问控制机制 为了确保即使是在嵌套多层子程序的情况下也能正确无误地找到所需的资源项,整个过程中始终以 EBP 寄存器作为参照点来进行寻址运算。这意味着即便 ESP 发生变动也不会干扰到对这些固定位置上的实体的操作[^5]。 例如在一个简单的加法函数 `add()` 中,两个整型输入参数会被分别存储至 `[ebp + 8]` 和 `[ebp + 12]` 地址单元之中,而计算所得的结果可直接赋值给同样处于低地址范围里的某个临时变量如 `[ebp - 4]` 来完成最终的任务处理逻辑[^3]. ```c int add(int a, int b) { int c = 0; c = a + b; return c; } ``` 此段源码对应的简化版伪汇编码可能是这样的: ```assembly _add: push ebp ; 保存原ebp mov ebp, esp ; 更新ebp指向本级底 sub esp, 4 ; 预留四个字节供内部使用(c) mov eax, DWORD PTR [ebp+8] ; 获取第一个参数a -> eax add eax, DWORD PTR [ebp+12] ; 将第二个参数b加入eax得到sum mov DWORD PTR [ebp-4], eax ; 把求和结果暂存在本地变量c里 mov eax, DWORD PTR [ebp-4] ; 加载回eax准备return leave ; 清理现场并重置ebp ret ; 结束本次调用跳转回去 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值