关键词:
前面的话:
作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
博主的码云gitee,平常博主写的程序代码都在里面。
1.寄存器
寄存器
是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC)。在中央处理器的算术及逻辑部件中,寄存器有累加器(ACC)。
本文不过多深入了解寄存器,只要知道寄存器集成在CPU之中和以下几个寄存器就可以了。
2.函数栈帧
2.1函数栈帧的概述
C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。
栈帧
也叫过程活动记录
,是编译器用来实现过程/函数调用的一种数据结构
。
函数栈帧的创建和销毁是基于栈所实现的。
所谓栈,是一种数据结构,具有先进后出的特点。在函数栈帧创建过程中,内存从高地址开始使用,越后面创建的函数栈帧或压栈数据,所存储的空间地址越低。
想要更深入了解栈
这一数据结构,欢迎访问博主另一篇文章:栈和队列介绍和基本功能从理论到实践
2.2函数栈帧创建过程
2.2.1被调用的main函数
main函数是会被其他函数调用的,在不同编译器中调用main的函数也不同。
在VS2019中,main函数会被下面几个编译器内置的函数链式访问。
首先,这个invoke_main函数会返回main函数的返回值。
static int __cdecl invoke_main()
return main(__argc, __argv, _get_initial_narrow_environment());
然后会有一个名叫main_result的int const类型变量接收,invoke_main函数的返回值,也就是main函数的返回值,最后这个main_result会被编译器其他函数所使用。
int const main_result = invoke_main();
函数栈帧的结构如下
esp为栈顶指针
ebp为栈底指针
它们共同维护函数栈帧
2.2.2函数栈帧创建与销毁的过程
对于函数栈帧的创建与销毁,我们以一个简单的程序为例。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
int d = a + b;
return d;
int main()
int a = 2;
int b = 6;
int c = 0;
c = add(a, b);
printf("%d\\n",c);
return 0;
由于编译器中有其他函数调用main,所以在main函数栈帧创建前,编译器中调用main的函数栈帧就已经创建了,esp,ebp会在如图位置
00892580 push ebp //ebp压栈
00892581 mov ebp,esp //将esp的值赋给ebp
00892583 sub esp,0E4h //将esp的值减0E4h,也就是为main函数栈帧分配空间
00892589 push ebx //ebx压栈
0089258A push esi //esi压栈
0089258B push edi //edi压栈
0089258C lea edi,[ebp-24h]
0089258F mov ecx,9
00892594 mov eax,0CCCCCCCCh
00892599 rep stos dword ptr es:[edi] //将main初始函数栈帧全部初始化为0CCCCCCCCh
0089259B mov ecx,89C003h
008925A0 call 0089130C //进入main函数
int a = 2;
008925A5 mov dword ptr [ebp-8],2 //ebp - 8就是a的位置,将a赋值为2
int b = 6;
008925AC mov dword ptr [ebp-14h],6 //同理ebp - 14h为b的地址将b赋值为6
int c = 0;
008925B3 mov dword ptr [ebp-20h],0 //ebp - 20h为c的地址,c赋值为0
c = add(a, b);
008925BA mov eax,dword ptr [ebp-14h] //传参,将b值传给add函数 ,先将b值传给eax
008925BD push eax //eax压栈
008925BE mov ecx,dword ptr [ebp-8] //传参,将a值传给add函数,先将a值传给ecx
008925C1 push ecx //ecx压栈
008925C2 call 00891023 //进入add
//带符号:008925C2 call _add (0891023h)
int add(int a, int b)
008917B0 push ebp //记录上一个ebp的地址
008917B1 mov ebp,esp //将ebp赋值成esp地址
008917B3 sub esp,0CCh //add函数栈帧
008917B9 push ebx
008917BA push esi
008917BB push edi
008917BC lea edi,[ebp-0Ch]
008917BF mov ecx,3
008917C4 mov eax,0CCCCCCCCh
008917C9 rep stos dword ptr es:[edi] //与main函数栈帧初始化同理,将add函数初始化为CC CC CC CC
008917CB mov ecx,offset _18BA86EA_test@c (089C003h)
008917D0 call @__CheckForDebuggerJustMyCode@4 (089130Ch)
//008917C9 rep stos dword ptr es:[edi]
//008917CB mov ecx,89C003h
//008917D0 call 0089130C
// int d = a + b;
//008917D5 mov eax,dword ptr [a]
//008917D8 add eax,dword ptr [b]
//008917DB mov dword ptr [d],eax
// return d;
//008917DE mov eax,dword ptr [d]
int d = a + b;
008917D5 mov eax,dword ptr [ebp+8] //将a赋值给eax
008917D8 add eax,dword ptr [ebp+0Ch] //将eax加上b,即2+6 = 8
008917DB mov dword ptr [ebp-8],eax //将eax=8赋值给d
return d;
008917DE mov eax,dword ptr [ebp-8] //将d的值赋值给寄存器eax
008917E1 pop edi //出栈edi
008917E2 pop esi //出栈esi
008917E3 pop ebx //出栈ebx
008917E4 add esp,0CCh //将add函数销毁,esp回到ebp的位置
008917EA cmp ebp,esp
008917EC call 00891235 //回到main
008917F1 mov esp,ebp //将ebp的地址给esp
008917F3 pop ebp //出栈ebp,让ebp指向上一次地址位置
008917F4 ret
008925C7 add esp,8 // 销毁两个形参,esp指向main函数栈顶
008925CA mov dword ptr [ebp-20h],eax //将eax(返回)值8赋值给ebp - 20h 也就是c
printf("%d\\n",c);
008925CD mov eax,dword ptr [ebp-20h] //将c值赋给eax
008925D0 push eax
008925D1 push 897BCCh
008925D6 call 008913A2
008925DB add esp,8
return 0;
008925DE xor eax,eax
//和add函数销毁一样,main函数销毁,结束程序
008925E0 pop edi
008925E1 pop esi
008925E2 pop ebx
008925E3 add esp,0E4h
008925E9 cmp ebp,esp
008925EB call 00891235
008925F0 mov esp,ebp
008925F2 pop ebp
008925F3 ret
函数栈帧的创建与销毁,带你了解代码底层原理(代码片段)
在学习C语言时,我们难免有许多疑问例如:(1)局部变量是怎么创建的?(2)为什么局部变量的值是随机的?(3)函数是怎么传参的?传参的顺序如何?(4)形参和实参是什么关系?(5)函数调用是怎么做的... 查看详情
问卷调查(代码片段)
...最大的困难就是自己吧,会经常拖延。2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?看MOOC,学校发的教材。不记得。2.2学了C语言,你分的清数组指针,指针数组;函数指针,指针... 查看详情
学好c语言,还需要掌握这个内功——函数栈帧的创建与销毁(代码片段)
学习本篇文章之前,你或许还有这些疑问:局部变量是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参的?传参的顺序是什么?形参和实参是什么关系?函数调用结束后怎么返回?看... 查看详情
调查问卷(代码片段)
...什么困难?A:专业方面没有什么困难。Q:2.1 你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?A:听课,看教程,实践,Google,百度。大约有1000行左右。Q:2.2 学了C语言,你分的清数组指针... 查看详情
调查问卷(代码片段)
...业学习方面情况如何,有什么收获,是否有什么困难?C语言还不是很熟练很多细节方面的东西没有做好2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?课本,网上查阅资料,实践2.2... 查看详情
c语言学习--函数栈帧的创建和销毁(代码片段)
前言在学习C语言的过程中,大家是否会存在一些困惑?比如:局部变量是如何创建的?为什么说局部变量未初始化时,其中存储的时随机值?函数到底时如何传参的?实参传递的顺序又是怎样的?... 查看详情
深入理解c语言从函数栈帧角度理解return关键字(代码片段)
初识函数栈帧如上图可见,函数在被调用的时候会现在栈上开辟一个空间,我们称之为栈帧,之后函数内部的变量在这块区域进行空间开辟。但是函数在调用的时候,怎么知道需要开辟多大空间呢??... 查看详情
问卷调查(代码片段)
...终不能超过老师的脚步,一直在跟行。2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?作业,实验和教材示例都有。大一上学期总和应该在5000-7000附近2.2学了C语言,你分的清数组指... 查看详情
c语言进阶顶级神功!函数栈帧的创建和销毁(代码片段)
本章目录温馨提示开篇介绍学前疑惑学前准备1.环境选择2.知识铺垫正文开始1.大致轮廓了解(源代码及反汇编)2.函数栈帧的创建和销毁总流程3.函数栈帧的创建3.1main函数的创建(分解)3.2Add函数的创建(分解... 查看详情
栈帧(代码片段)
C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先,栈是从高地址向低地址延伸的。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶... 查看详情
go语言必须支持多返回值函数,你知道为什么吗?(代码片段)
大多数编程语言的函数(方法)都只能返回一个值,这种函数也是在数学中的标准定义,如y=f(x),后面的f(x)不管多复杂,y永远只有一个。不过有少数编程语言,函数可以返回多个值... 查看详情
go语言必须支持多返回值函数,你知道为什么吗?(代码片段)
大多数编程语言的函数(方法)都只能返回一个值,这种函数也是在数学中的标准定义,如y=f(x),后面的f(x)不管多复杂,y永远只有一个。不过有少数编程语言,函数可以返回多个值... 查看详情
问卷调查
...如何,有什么收获,是否有什么困难?答:收获了一些c语言的代码知识,有些知识需要反复咀嚼不太好轻松消化2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?答:作业,不记得了2... 查看详情
c语言之函数调用及栈帧分析(代码片段)
一.前言 每一次函数调用都是一个过程。这个过程我们通常称之为:函数的调用过程。这个过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存,现场保护。这块栈空间就是函数栈帧。实验代码:#inc... 查看详情
函数栈帧的创建与销毁(代码片段)
栈帧的创建与销毁什么是栈帧C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一... 查看详情
4.27
...有什么困难?学会了helloworld的写法,没有2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?作业,1002.2学了C语言,你分的清数组指针,指针数组;函数指针,指针函数这些概念吗?分不... 查看详情
调查问卷(代码片段)
...自己是如何投入这个专业的学习的?答:想着自己能精通语言然后能开发出一个游戏。1.4结合过去的一学期,你目前在专业学习方面情况如何,有什么收获,是否有什么困难?答:收货的话还行,逻辑思维稍许变强了些吧,困难... 查看详情
问卷调查
...否有什么困难?收获很多,困难也很多2.1你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码?课堂学习2.2学了C语言,你分的清数组指针,指针数组;函数指针,指针函数这些概念吗?不2.3... 查看详情