今天我们来探讨下 C 语言中的数据存储方式。在程序中,数据的存储方式无外乎分为栈、堆以及静态存储区。我们分别来看看这三种方式,看看有何区别。
A、程序中的栈
栈是现代计算机程序里最为重要的概念之一,栈是用于维护函数调用上下文,同样函数中的参数和局部变量存储在栈上。栈保存了一个函数调用所需的维护信息,如下图所示
那么每次函数调用都对应着一个栈上的活动记录:a> 调用函数的活动记录位于栈的中部;b> 被调函数的活动记录位于栈的顶部。
从 main() 开始运行,我们看到有两个指针 ebp 和 esp。
那么当 main() 调用 f() 时,ebp 就往前走四个字节,指向原来 esp 的位置。esp 也继续向前走
当从 f() 调用总返回 main() 时,ebp 和 esp 都向回退四个字节。
那么我们看到函数调用时,对应的栈空间在函数返回前是专用的。当函数调用后栈空间将被释放,数据不再有效。下图更形象的表示了
下来我们以代码为例进行分析
#include <stdio.h> int* g() { int a[10] = {0}; return a; } void f() { int* pointer = g(); } int main() { f(); return 0; }
我们看到在函数 g() 中定义了一个数组,但是我们返回了它的地址,也就是返回了局部数组的地址。在 f() 中调用了 g(),这样肯定会出问题,我们来看看编译结果
我们看到编译器已经给出了警告,那么这么操作肯定是不安全的。
B、程序中的堆
那么什么是堆呢?堆是程序中一块预留的内存空间,可由程序自由使用,堆中被程序申请使用的内存在被主动释放前将一直有效。那么我们为什么有了栈还需要堆呢?栈上的数据在函数返回后就会被释放掉,无法传递到函数外部,如:局部数组。那么堆则不一样,如果我们不去主动释放,它就一直有效,但是也就造成了一个问题,如果我们只申请不去释放堆,到最后堆用完了程序便会崩溃。
那么我们在程序中怎么来申请堆呢?在 C 语言程序中通过库函数的调用来获得堆空间。对应的头文件是 malloc.h;malloc 是以字节的方式动态申请堆空间;free 是将堆空间归还给系统。系统对堆空间的管理方式有这么几种:空闲链表法、位图法以及对象池法等。
下图是空闲链表管理法的示意图
如果我们需要申请 4 字节的话,根据这个表来看,我们便会申请到跟它最匹配的,便是 5 字节了。所以有时我们申请的空间会比我们所需的大一点。
C、程序中的静态存储区
静态存储区是随着程序的运行而分配空间,它的生命周期直到程序运行结束。在程序的编译器静态存储区的大小就已经确定,主要用于保存全局变量和静态局部变量,它保存的信息最终会保存到可执行程序中。
下来我们以代码为例来进行分析
#include <stdio.h> int g_v = 1; static int g_vs = 2; void f() { static int g_vl = 3; printf("&g_vl = %p\n", &g_vl); } int main() { printf("&g_v = %p\n", &g_v); printf("&g_vs = %p\n", &g_vs); f(); return 0; }
我们看到分别定义了三个变量,第3行是 int 型的全局变量,第5行是加 static 修饰的 int 型变量,第9行是加 static 修饰的函数内的局部变量。我们分别来打印下三个变量的地址,看看他们有什么关系
那么我们看到虽然他们三个类型不同,但是地址是连续的,也就证明他们三个是分布在同一个数据区的,便是静态存储区啦。通过本节对栈、堆以及静态存储区的学习,总结如下:1、栈区主要用于函数调用的使用;2、堆区主要是用于内存的动态申请和归还;3、静态存储区用于保存全局变量和静态变量。
欢迎大家一起来学习 C 语言,可以加我QQ:243343083。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。