C语言程序编译生成的二进制映像文件由栈,堆,数据段和代码段组成,如下图所示:
1.栈区(stack):由编译器自动分配释放,栈主要用于存储局部变量、函数参数、函数调用和返回信息等。其操作方式类似于数据结构中的栈。
2.堆区(heap):一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。注堆和数据结构中的堆栈不一样,其类是与链表。
3.程序代码区:存放函数体的二进制代码。
4.数据段:由只读数据段、已初始化的读写数据段和未初始化段(BSS)组成。
(1)只读数据段
只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。一般是const修饰的变量以及程序中使用的文字常量一般会存放在只读数据段中。
(2)已初始化的读写数据段
已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并且有初值,以供程序运行时读写。在程序中一般为已经初始化的全局变量,已经初始化的静态局部变量(static修饰的已经初始化的变量)。
(3)未初始化段(BSS)
未初始化数据是在程序中声明,但是没有初始化的变量,这些变量在程序运行之前不需要占用存储器的空间。与读写数据段类似,它也属于静态数据区。但是该段中数据没有经过初始化。未初始化数据段只有在运行的初始化阶段才会产生,因此它的大小不会影响目标文件的大小。在程序中一般是没有初始化的全局变量和没有初始化的静态局部变量。
栈和堆的区别
1.存储位置区别
栈是一种线性数据结构,存储在计算机内存的栈区,它采用了一种"先进后出"(Last-In-First-Out,LIFO)的原则。堆是一种动态分配的内存区域,存储在计算机内存的堆区,它的分配和释放是由程序员来管理的。
2. 分配方式
栈空间的分配和释放是由编译器自动完成,速度很快,但大小是固定的,不够灵活。堆的分配和释放是由程序员手动操作的,可以根据需要动态分配和释放内存空间。
3. 存储内容
栈主要用于存储局部变量、函数参数、函数调用和返回信息等,它的生命周期与所在的函数密切相关,函数执行结束后,栈上的数据就会被自动释放。堆主要用于存储程序运行时动态分配的数据,例如通过new或malloc函数分配的内存,需要手动释放。
4. 大小限制
栈的大小是有限的,通常由操作系统设置,默认情况下栈的大小较小(例如1MB)。而堆的大小受限于操作系统的虚拟内存空间,一般要比栈的大小大得多。
总的来说,栈是一种高效的数据结构,适合存储局部变量等小数据,内存管理由编译器自动完成;而堆是一种灵活的数据结构,适合存储动态分配的大块数据,内存管理由程序员负责。在使用过程中,需要根据具体的需求选择合适的数据结构。
示例程序
使用栈
#include
void foo() {
int num = 10; // 局部变量,存储在栈上
printf("Num in foo: %d\n", num);
}
int main() {
foo();
return 0;
}
在这个示例中,`num` 是一个局部变量,它存储在栈上。当函数`foo()`被调用时,`num`被分配在栈上。当`foo()`函数执行完毕后,栈中的数据会被自动释放。
使用堆
#include
#include
int main() {
int* num_ptr = (int*)malloc(sizeof(int)); // 动态分配内存空间,存储在堆上
if (num_ptr == NULL) {
printf("Memory allocation failed\n");
return -1;
}
*num_ptr = 20; // 在堆上存储数据
printf("Num in heap: %d\n", *num_ptr);
free(num_ptr); // 释放堆内存
return 0;
}
在这个示例中,`num_ptr`是一个指向整型变量的指针,通过`malloc()`函数动态分配了一块大小为`sizeof(int)`的内存空间,该空间存储在堆上。我们可以通过`num_ptr`访问和修改这块堆内存中的数据。使用完堆内存后,我们需要调用`free()`函数来释放堆内存,以防止内存泄漏。
在堆内存分配的代码示例中,`if`语句的作用是检查内存分配是否成功。在C语言中,当使用`malloc()`函数动态分配内存时,如果请求的内存空间不可用或不足,`malloc()`函数将返回一个空指针(`NULL`)。因此,在使用动态分配的内存之前,我们需要检查分配是否成功。
在示例中,`if`语句的条件判断 `num_ptr == NULL` 检查了`num_ptr`是否为`NULL`,即检查内存分配是否失败。如果分配失败,该分支下的代码将会执行,打印 "Memory allocation failed" 的错误信息,并返回一个非零值表示程序异常退出。
这个检查过程是很重要的,因为如果`malloc()`分配失败,后续对内存空间的操作可能会导致未定义的行为或崩溃。通过检查返回值,我们可以及时发现内存分配问题,并进行适当的处理。
(注意:在使用堆时要注意内存分配和释放的配对,以防止内存泄漏和悬空指针等问题。)
参考内容
[1] 业百科的文章《c语言中堆和栈的区别》,文章链接:https://www.yebaike.com/22/3045151.html
[2] 博客园作者嵌入式小白-小黑的文章《【C语言】一文搞懂C语言中堆和栈的区别》,文章链接:
https://www.cnblogs.com/Goforyouqp/p/17606937.html
[3] 冀博的文章《浅析C语言中堆和栈的区别》,文章链接:
https://www.jb51.net/article/87321.htm
[4] 三秦科技网的文章《C语言中堆与栈的区别 堆和栈的概念和区别》,文章链接:
http://tech.zhongtouwang.com.cn/tech/20220526/109796.html
本文内容来源于网络,仅供参考学习,如内容、图片有任何版权问题,请联系处理,24小时内删除。
作 者 | 郭志龙
编 辑 | 郭志龙
校 对 | 郭志龙