前言
路在脚下, 秃在前方!!
究竟什么是栈区, 堆区,静态区, 常量区, 代码区, 每次听到这些都是一知半解, 稀里糊涂的.
今天整理一下, 以后忘了也可以回来翻阅.
我在写这篇博客的时候,也查阅了相关资料,包括其他博主的一些博文,发现其中还是有些错误的。我说这个是希望大家在查阅资料的时候依旧保持辩证思维,多去验证。实践是检验真理的唯一标准。
目录
内存的主要划分
数据区
堆区
栈区
常量区
分析一段代码
内存主要分为: 代码区, 数据区(静态区), 栈区, 堆区, 常量区.
代码区:也称文本区, 用于存放程序的指令. 这个区域通常是只读的, 存储可执行文件的机器指令. 就比如, 程序经过编译、链接后生成的可执行文件就存放在这里。(只能解释到这里了,目前章鱼哥对代码区了解的是在不多,也不敢多说)
数据区:也称静态区、全局区,用来存放静态变量和全局变量。内部还分为初始化数据区和非初始化数据区。
堆区: 是动态分配内存的区域,用于存放程序运行时动态分配的内存空间。在堆上分配的内存空间需要程序员手动进行管理,包括申请、释放等操作。
栈区:栈用于存放函数调用时的局部变量,函数参数,返回地址等信息。栈是一种先进后出的数据结构,内部空间的管理是由编译器自动管理。
常量区:也称为只读区,用于存放程序中的常量数据,这个区域是只读的,不允许对其中的数据进行修改。
初始化数据区:也称数据段,用于存放已初始化的全局变量和静态变量。在程序运行前,这些变量会被赋予特定的初始值。
非初始化数据区:也称BBS段,存放未初始化的全局变量和静态变量。在程序运行前,这些变量会被系统自动初始化为0或者空值。
得证:静态变量和全局变量若未初始化,在程序运行前会自动初始化0。也说明静态区可读可写。
得证:局部变量未初始化,会在编译阶段直接报错。
猜想:静态变量和全局变量存放在数据区,而局部变量存放在栈区,两种情况的不同,应该与这一点有关。
堆区是动态分配内存的区域,通常用malloc()函数来申请调用该区域空间。相关函数还有calloc()函数,realloc()函数。简单介绍下:
malloc()函数,是动态申请一块儿内存空间,空间内的存储值为随机值。
calloc()函数,是动态申请一块儿内存空间,并对其进行初始化。
realloc()函数,是对已申请的空间动态扩容。扩容成功,则返回空间的起始地址,扩容失败则返回NULL,会直接丢弃原有空间。所以使用realloc()函数需要额外注意。
free()函数,释放堆区已分配空间。free()函数参数也需要额外注意,
如果free()函数释放的空间不是动态内存开辟的,那么free()的行为是未定义的。
如果free()函数参数p指向NULL,则什么也不发生。
行为未定义:是指C语言标准未做规定的行为,会由编译器自行处理。(简单地说,可能会发生未知的事情,也可能什么也不发生)
可能导致的问题:内存泄漏,野指针;
1,在申请了堆区内存后,若忘记释放内存,则很容易导致内存泄漏。
2,在释放内存后,若未将指针置空,就会造成野指针。错误的使用野指针也可能造成内存泄漏。
内存泄漏:是指在程序运行过程中,由于程序设计或者代码实现问题,导致程序申请的内存无法被释放,最终导致系统内存不足,甚至系统崩溃。为什么说会导致系统内存不足呢,因为计算机的每一块存储空间都有一个状态位来标记该空间是否已使用。如果不释放空间,空间的状态位会一直显示已使用,导致可分配的空间变小。
栈区是程序在运行时用于管理函数调用和局部变量的一块内存区域。实现函数调用、局部变量存储、内存管理三个功能。
需要注意的是栈区的大小是有限的,并且由系统或编译器设置。当栈区空间不足时,可能对导致栈溢出的问题,因此在编写程序时需要注意控制递归深度和局部变量的大小,避免栈溢出。
内存管理:栈区由编译器自动管理,分配和释放内存空间的过程是透明的。当一个函数被调用时,编译器为其分配一块大小适当的内存空间(函数栈帧),并在函数执行完毕后自动释放该内存空间并收回。
局部变量存储:每当一个函数被调用时,局部变量的内存空间会在栈帧上被分配,并在函数执行完毕后释放。这种自动分配和释放的机制使得局部变量的生命周期与函数的执行项关联。由于栈帧空间有限,所以局部变量的大小和数量都会对栈帧空间产生影响,过多或过大可能会导致栈溢出。
函数调用:当一个函数被调用时,会创建一个新的函数栈帧并被推入调用栈中,函数栈帧用于保存该函数执行所需要的上下文信息。通常包含返回地址、参数、局部变量、返回值,其他上下文信息(寄存器值,异常处理信息)。当函数执行完毕后,栈帧从调用栈中被弹出,控制流返回到调用处。这种栈帧的创建和销毁机制使得函数调用能够正确的传递参数、保存上下文信息,并实现函数的嵌套调用和返回。
调用栈:是一种用于管理函数调用和返回的数据结构。是由一系列函数栈帧按照特定的顺序组成的。当一个函数被调用时,会在调用栈中创建一个新的函数栈帧,并将其推入栈顶。当函数内部调用另一个函数时,新的函数栈帧会被创建并推入栈顶,而原先的函数栈帧则暂时保存在栈中。这样,程序就可以在被调用的函数执行完毕后,回到原先调用它的函数,继续执行之前的代码。调用栈保证了函数调用和返回的顺序正确。它使得程序能够跟踪和管理多个函数之间的执行流程,并确保在函数返回时能够恢复正确的执行状态。
函数返回:当一个函数执行完毕后,对应的函数栈帧会从调用栈中被弹出。控制权会返回给上一层调用的函数。这个过程称为函数返回。
用于存储程序中的常量数据的内存区域,包括各种常量。这些常量的值在编译时就确定,并在程序加载到内存时被初始化。常量区的内容是只读的,不能被修改。
常量的初始化:是指在编程中,程序员给一些数值定义了一个名字。
比如:
在C/C++中:
const int MAX_VALUE = 100;
#define MAX 20
Java:
final int MAX_VALUE = 100;
常量的初始化是一次性的操作,只能在定义时或者声明时进行。一旦常量被初始化,其值就不能被改变。
int arr[10] = 0;
int a = 0;
int main()
{
char arr1[] = { "abcdef" };
char* p1 = "abcdef";
char* p2 = "abcdef";
return 0;
}
数组arr 和 变量a都是存放在数据区(静态区)的;字符串“abcdef”是存放在常量区;数组arr1是将字符串拷贝了一份,并且存储在栈区;指针p1 和 指针p2 都是存放在栈区,并且指向同一个存放在常量区的字符串。
我是专注学习的章鱼哥~