内存四区模型浅析——C语言

栏目: C · 发布时间: 5年前

内容简介:在C语言中,我们将程序在运行时所占用的内存资源分为四个区域(堆区、栈区、全局区、代码区),今天在温习C语言时查漏补缺,做一下记录。需要注意,文中所谈及的堆栈等指的是内存中的堆区与栈区,与数据结构中所谈的堆栈(数据结构中”堆栈”即”栈”)没有必然的联系,是两个完全不同的概念。前者指明数据存储在哪种内存区之上,后者是组织数据的一种手段。参考堆区(heap) 主要用于动态内存分配,如

C语言 中,我们将程序在运行时所占用的内存资源分为四个区域(堆区、栈区、全局区、代码区),今天在温习C语言时查漏补缺,做一下记录。需要注意,文中所谈及的堆栈等指的是内存中的堆区与栈区,与数据结构中所谈的堆栈(数据结构中”堆栈”即”栈”)没有必然的联系,是两个完全不同的概念。前者指明数据存储在哪种内存区之上,后者是组织数据的一种手段。

参考

内存分配

堆区

堆区(heap) 主要用于动态内存分配,如 malloc , new ,申请时需要指定大小。堆上动态分配的内存在使用完毕后,需要通过程序主动释放内存(如 freedelete ),否则程序将在最后才释放掉动态内存,易出现内存泄漏。一般来说,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,并立即将指针置位 NULL ,防止产生野指针。(@ Captain–Jack )

栈区

栈区(stack) 主要用于存储函数内部局部变量(如 char a; ),与堆不同,栈上空间的开辟与释放一般由操作系统自己控制。

全局区

全局区(global)也称作静态区,主要用于存储常量和全局变量,细分有一个常量区, 字符串常量和其他常量。该区域在程序运行完毕后由操作系统进行释放。

代码区

代码区(code) 存放函数体的二进制代码,也是由操作系统进行管理。这里不深入探讨,了解有这个区即可。

示例代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//栈区
int stackArea()
{
	printf("-----------------------栈区---------------------------\n");
	int a = 10;
	printf("子函数中a的地址 %d\n", &a);
	return a;
}

//堆区
int*heapArea()
{
	printf("-----------------------堆区-----------------------------\n");
	int *chs = NULL;

	chs = (int*)malloc(sizeof(int) * 10);
	for (int i = 0; i<10; i++)
	{
		*(chs + i) = i;
	}
	printf("子函数中chs的地址是: %d\n", chs);
	return chs;
}

//全局区
char*globalArea()
{
	printf("----------------------全局区---------------------------\n");
	char *str = "abcde";
	printf("子函数中str的地址是: %d\n", str);
	return str;
}

int main()
{

	int a_main = stackArea();
	printf("main函数中 a_main的地址是: %d\n", &a_main);

	int* chs_main = heapArea();
	printf("main函数中 chs_main的地址是: %d\n", chs_main);
	free(chs_main);

	char* str_main = globalArea();
	printf("main函数中 str_main的地址是: %d\n", str_main);

	system("pause");
	return 0;
}

运行所得结果为:

-----------------------栈区---------------------------
子函数中a的地址 15726708
main函数中 a_main的地址是:  15726960
-----------------------堆区-----------------------------
子函数中chs的地址是: 17560528
main函数中 chs_main的地址是: 17560528
----------------------全局区---------------------------
子函数中str的地址是: 8616960
main函数中 str_main的地址是: 8616960
请按任意键继续. . .

结果分析

  • stackArea() 函数内 a 的地址为 15726708 ,在 main 中调用该函数得到的 a_main 的地址为 7339248,这是因为栈区变量的生命周期短,短到当 main 调用 stackArea() 结束后,15726708 地址便立即被释放,在main函数中,重新分配地址来储存 a_main。
  • heapArea() 函数内 chs 的地址为动态分配的地址 17560528,堆区变量生命周期长,需要主动释放或者程序运行完毕后才释放。 因此,在 main 函数调用 heapArea() 结束后,chs 地址空间不变,直到 free(chs_main) 才释放。
  • globalArea() 函数内 str 的地址为 8616960,因其为字符串,储存于全局区,所以地址不变,生命周期为整个程序的运行期间。当程序退出后由操作系统进行释放处理。

堆栈区别

转载于 @Captain–Jack

堆和栈的主要区别由以下几点:

1、管理方式不同;

2、空间大小不同;

3、能否产生碎片不同;

4、生长方向不同;

5、分配方式不同;

6、分配效率不同;

管理方式

对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由 程序员 控制,容易产生memory leak。

空间大小

一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改: 打开工程,依次操作菜单如下:Project->Setting->Link,在 Category 中选中 Output ,然后在 Reserve 中设定堆栈的最大值和 commit 。 注意: reserve 最小值为 4Byte; commit 是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

碎片问题

对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

生长方向

对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式

堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloca 函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。

分配效率

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。 虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生意想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编程珠玑(第2版•修订版)

编程珠玑(第2版•修订版)

[美] Jon Bentley 乔恩•本特利 / 黄倩、钱丽艳 / 人民邮电出版社 / 2014-12 / 39

历史上最伟大的计算机科学著作之一 融深邃思想、实战技术与趣味轶事于一炉的奇书 带你真正领略计算机科学之美 多年以来,当程序员们推选出最心爱的计算机图书时,《编程珠玑》总是位于前列。正如自然界里珍珠出自细沙对牡蛎的磨砺,计算机科学大师Jon Bentley以其独有的洞察力和创造力,从磨砺程序员的实际问题中凝结出一篇篇不朽的编程“珠玑”,成为世界计算机界名刊《ACM通讯》历史上最受欢......一起来看看 《编程珠玑(第2版•修订版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具