这个模块临近C语言的边界,学起来需要一定的时间,不过当我们知道这些知识,在C语言函数这块我们看到的不仅仅是表象了,可以真正了解函数是怎么调用的。不过我的能力有限,下面的的知识若是不当,还请各位斧正。
函数大家应该都很熟悉了,这里就不细说了。
我们看看就行
ret_type fun_name(para1, * )
{
statement; //语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数
我们一直说 :“全局变量的生命周期是所在的整个程序”、“static修饰的变量的生命周期变长了”、以及“最重要的临时变量出函数就要被销毁”。不过我们要知道这是因为什么。
在C语言中我们所创建的每一个变量都会有自己空间的的存储类别,就比如汽车一般不会停在高楼那样,每一个事物都会有自己的集合。
看一下代码,来验证一下
#include
#include
int g_val1 = 10;
int g_val2 = 10;
int g_val3;
int g_val4;
int main()
{
const char* str = "abcdef";
printf("code: %p\n", main);
printf("read only : %p\n", str);
printf("init g_val1 : %p\n", &g_val1);
printf("init g_val2 : %p\n", &g_val2);
printf("uninit g_val2 : %p\n", &g_val3);
printf("uninit g_val2 : %p\n", &g_val4);
char* p1 = (char*)malloc(sizeof(char*) * 10);
char* p2 = (char*)malloc(sizeof(char*) * 10);
printf("heap addr : %p\n", p1);
printf("heap addr : %p\n", p2);
printf("stack addr : %p\n", &str);
printf("stack addr : %p\n", &p1);
printf("stack addr : %p\n", &p2);
return 0;
}
函数的调用与CPU中的寄存器有很大关系,下面有一些基本知识
看了这么多知识,我们一定会感到很是枯燥,觉得这和函数栈帧一点关系都没有,不要着急,下面就开始我们正式的内容。
我们知道 main函数也是一个函数,它也是能够被调用,所以main函数也会形成栈帧。
样例代码
int MyAdd(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int x = 0xA;
int y = 0xB;
int z = 0;
z = MyAdd(a, b);
printf("z = %d\n",z);
return 0;
}
我将汇编代码复制下来,我们一步一步分析这些东西
int main()
{
int main()
{
int main()
{
00821E40 push ebp
00821E41 mov ebp,esp
00821E43 sub esp,0E4h
00821E49 push ebx
00821E4A push esi
00821E4B push edi
00821E4C lea edi,[ebp-24h]
00821E4F mov ecx,9
00821E54 mov eax,0CCCCCCCCh
00821E59 rep stos dword ptr es:[edi]
00821E5B mov ecx,82C003h
00821E60 call 0082130C
int x = 0xA;
00821E65 mov dword ptr [ebp-8],0Ah
int y = 0xB;
00821E6C mov dword ptr [ebp-14h],0Bh
int z = 0;
00821E73 mov dword ptr [ebp-20h],0
z = MyAdd(x, y);
00821E7A mov eax,dword ptr [ebp-14h]
00821E7D push eax
00821E7E mov ecx,dword ptr [ebp-8]
00821E81 push ecx
00821E82 call 008211E5
00821E87 add esp,8
00821E8A mov dword ptr [ebp-20h],eax
printf("z = %d\n", z);
00821E8D mov eax,dword ptr [ebp-20h]
00821E90 push eax
00821E91 push 827BCCh
00821E96 call 008213A2
00821E9B add esp,8
return 0;
00821E9E xor eax,eax
}
00821EA0 pop edi
00821EA1 pop esi
00821EA2 pop ebx
00821EA3 add esp,0E4h
00821EA9 cmp ebp,esp
00821EAB call 00821235
00821EB0 mov esp,ebp
00821EB2 pop ebp
00821EB3 ret
int x = 0xA;
01011E65 mov dword ptr [ebp-8],0Ah
//在ebp-8处在开辟一个空间,将x的值放进去
int y = 0xB;
01011E6C mov dword ptr [ebp-14h],0Bh
//在ebp-14处在开辟一个空间,将y的值放进去
int z = 0;
00821E73 mov dword ptr [ebp-20h],0
//在ebp-20处在开辟一个空间,将z的值放进去
可以看出,x、y、z 的空间是不连续的 ,这是VS保护机制, 防止一些程序员猜测对应的地址。
00821E7A mov eax,dword ptr [ebp-14h]
把ebp-14(也就是y) 赋值给eax
00821E7D push eax
push命令将eax的值放入栈中,同时栈顶的位置发生变化,变化的大小是4个字节,因为y是int型
push之后的栈顶
00821E7E mov ecx,dword ptr [ebp-8]
00821E81 push ecx
这里先说一下call命令的作用
00821E82 call 008211E5
jmp前
jmp后
现在我们总算进入了MyAdd()函数了,
画一下我们的栈帧图
由于篇幅有限,我们先说到这里,下一篇接着说MyAdd()函数内部的事情。