深入解析堆与栈的区别:全局堆、局部堆及线程栈的详细比较

首先我们来科普一下:

深入解析堆与栈的区别:全局堆、局部堆及线程栈的详细比较

什么是堆?说到堆,又忍不住说到了栈!什么是 栈?

栈是什么:栈是线程所独有的,它用于保存线程的运行状态以及局部自动变量。栈在线程开始时进行初始化,各个线程的栈是相互独立的。每个函数都拥有其自身的栈,栈被用于在函数之间传递参数。操作系统在进行线程切换时,会自动地切换栈,也就是切换 SS/ESP 寄存器。栈空间无需在高级语言中进行显式的分配和释放操作。

C语言程序编译的内存分配,堆与栈的区别:

堆通常是由程序员来进行分配和释放的,如果程序员不进行释放,那么在程序结束的时候,可能会由操作系统来进行回收。需要注意的是,这里说的是可能回收,而不是一定回收。再次强调,一定要记得进行释放操作!

栈区(stack) :

在 Windows 下,栈内存被分配了 2M,这是一个确定的常数。当超出这个限制时,就会提示 stack overflow 错误。

编译器会自动进行分配和释放。它主要用来存放函数的参数值以及局部变量值等。

堆区(heap):程序员手动分配释放,操作系统80%内存

全局区或静态区:这里会存放全局变量以及静态变量;在程序结束的时候,会由系统来进行释放;它又分为全局初始化区和全局未初始化区;

字符常量区:常量字符串放与此,程序结束时由系统释放;

程序代码区:存放函数体的二进制代码。

栗子:

定义整型变量 a 并初始化为 0,a 位于全局初始化区;定义字符变量 p1,p1 位于全局未初始化区;在主函数 main 中,定义整型变量 b,b 位于栈区;定义字符数组 s 并初始化为 "bb",s 位于栈区;定义字符变量 p2,p2 位于栈区;定义字符变量 p3 并初始化为 "123",其中 "123\0" 位于常量区,p3 位于栈区;定义静态整型变量 c 并初始化为 0,c 位于全局区;通过 malloc 函数分配 10 个字节的区域给 p1,该区域位于堆区;使用 strcpy 函数将 "123" 复制到 p1 中,"123\0" 位于常量区,编译器可能会优化为和 p3 的指向同一块区域。

栈内存

在函数 stackFun 中:定义了一个大小为 1024 的整型数组 a;栈内存会自动释放。

堆内存

最后使用`free`函数释放了分配的内存`p`。

在堆内存中进行分配操作,分配的内存大小为 40M。一直处于循环状态,循环条件为 1,在每次循环中会使程序暂停 1000 毫秒,然后执行 stackFun 函数。最后通过 getchar 函数获取字符输入。

创建一个数组,在程序运行过程中可以动态指定其大小,能够随意开辟指定大小的内存以供使用,这就如同 Java 中的集合。静态内存分配时,分配的内存大小是固定的。存在的问题如下:其一,很容易超出栈内存的最大值;其二,为防止内存不够用而开辟更多内存,容易导致内存浪费。

在程序运行期间,进行动态内存分配,可动态地指明需要使用的内存的大小,并且需要手动进行释放,当释放之后,这些内存是可以再次被使用的。

函数:calloc() 分配内存空间并进行初始化。calloc() 函数用于动态地分配内存空间且初始化为 0 ,它的原型为:

调用 calloc 函数,该函数需要传入两个参数,一个是元素的数量 num,另一个是每个元素的大小 size。

calloc() 在内存中进行动态分配。它分配的是 num 个长度为 size 的连续空间。并且会将每一个字节初始化为 0。所以它的结果是分配了 numsize 个字节长度的内存空间,且每个字节的值都为 0。

分配成功时会返回指向该内存的地址,而分配失败时则会返回 NULL。

函数:malloc() 动态地分配内存空间

malloc()函数用于动态地分配内存空间。如果你不了解动态内存分配,可查看:C 语言动态内存分配及变量存储类别。其原型为:

void* malloc (size_t size);

应用在程序中代码如下:

在 main 函数中,通过静态内存分配来创建数组,此数组的大小是固定的。首先定义了一个变量 i 并赋值为 10,接着又定义了一个数组 a,其大小由变量 i 决定。然后要求用户输入数组的长度,通过 scanf 函数获取用户输入的值并存储在变量 len 中。接着使用 malloc 函数开辟内存,大小为 len 乘以 sizeof(int),得到的内存地址存储在变量 p 中,p 即为数组的首地址,也就相当于数组的名称。之后通过一个循环,从 i 为 0 开始,当 i 小于 len 时,给数组元素赋值,使用的就是刚刚开辟出来的内存区域。< len - 1; i++){p[i] = rand() % 100;使用 printf 函数输出 p 数组中第 i 个元素的值及其地址的十六进制表示形式,分别以十进制整数和十六进制数的形式输出,输出格式为"%d,%#x\n",其中 p[i] 表示数组 p 中的第 i 个元素,&p[i] 表示 p 数组中第 i 个元素的地址。 }//手动释放内存 //free()释放动态分配的内存空间 free(p); getchar();}

realloc 重新分配内存

接着进入一个循环,只要 `i` 小于 `len` 就执行循环体中的操作。< len; i++){p[i] = rand() % 100;printf("%d,%#x\n", p[i], &p[i]); } int addLen; printf("输入数组增加的长度:"); scanf("%d", &addLen); //内存不够用,扩大刚刚分配的内存空间 //1.原来内存的指针 2.内存扩大之后的总大小int* p2 = realloc(p, sizeof(int) * (len + addLen)); if (p2 == NULL){printf("重新分配失败,世界那么大,容不下我。。。"); }

新分配内存的两种情况:

//缩小,缩小的那一部分数据会丢失

如果申请失败,就返回 NULL,而原来的指针仍然有效。

接着上面的代码进行重新赋值,先令 i 等于 0 ;然后输出“--------------------------”;接着进入循环,在循环条件中,当 i 满足一定条件时执行循环体内容。< len + addLen; i++){p2[i] = rand() % 200;printf("%d,%#x\n", p2[i], &p2[i]); } //手动释放内存 if (p != NULL){free(p);p = NULL; }if (p2 != NULL){free(p2);p2 = NULL; } getchar();}

内存分配的几个注意细节

不能进行多次释放操作;释放完成后(即便指针仍有值),需将指针置为 NULL,以此标志释放已完成;存在内存泄露情况,即当 p 重新赋值后再进行 free 操作,实际上并未真正释放内存。

void main(){

int len;

printf("输入数组的长度:");

scanf("%d", &len);

​

分配了一块内存空间,该内存空间的大小为 len 乘以 sizeof(int),并将指向这块内存空间的指针赋值给了指针变量 p,p 是一个指向 int 类型的指针。

int i = 0;

for (; i < len; i++){

p[i] = rand() % 100;

printf("%d,%#x\n", p[i], &p[i]);

​

if (p != NULL){

free(p);

p = NULL;

​

getchar();

你可能想看:
« 上一篇
下一篇 »

相关推荐

FreeBasic:强大免费开源的64位basic编译器及应用优势

2025年05月10日

82阅读

脊柱侧弯矫正方法大盘点:运动康复、支具矫正等多种方式

2025年05月09日

87阅读

如何在winForm标题栏去掉标题文字且任务栏图标正常显示

2025年05月09日

86阅读

十一黄金周促销广告词大全,国庆购物狂欢等你来

2025年05月09日

78阅读

关闭快速编辑模式及插入模式,设置控制台指定文字颜色举例

2025年05月09日

63阅读

黄金搭档低糖高钙饮品包装及成分功效全解析,适合人群要知晓

2025年05月09日

91阅读