💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍
文章目录
- 一、C++;内存的五大分区
- 二、栈区和堆区详解
- 1、栈区
- 2、堆区
- 3.⛔️【注意】
- 三、指针的存储位置
- 四、例题巩固
- 五、类的成员函数和静态成员函数
- 一、公共代码区详解
- 2.什么是第二分区?
- 二、函数和函数代码存储位置
前言:
通过文章,我们主要学习在C++\C 中,各个对象,变量,实参等,以及Static修饰的函数或变量的存储位置。 并通过例题来详细讲解。
一、C++;内存的五大分区
- 栈区、 堆区、内存映射段、静态区/数据段、代码段
🔸内核空间:这个区域是系统内部的区域,我们是不可编辑的;
🔸栈区:(主要存储函数运行时而分配的局部变量,函数参数,返回数据,返回地址,指针
,const局部变量
)
🔸内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信
🔸堆区:由程序员可以自行分配的一块空间。如malloc、new。
🔸 数据段:存储全局变量,静态变量.例如:sattic修饰的静态变量。数据段又分BSS
段和DATA
段 。
其中BSS存放的是程序员编写的未初始化的全局变量和静态变量(这其中C和C++的BSS还有区别,区别就是:C中的BSS段分为高地址和低地址,高地址是存放全局变量,低地址是存放静态未初始化);
DATA存放已经初始化的全局变量、静态变量和常量。
🔸代码段:又被称为文本段
。主要存储程序指令、代码
、只读常量
。代码段中存放函数
(类的成员函数和全局函数)的二进制代码,并且这个区域是只读
区域;
二、栈区和堆区详解
1、栈区
栈区向地址减小的方向增长
- 在执行函数时,函数内部变量的存储在栈上。函数执行结束时,系统自动回收。
- 栈区:大小大约只有2MB。分配容量有限
- 执行效率很高:栈内存分配运算内置于处理器的指令集中。
2、堆区
堆区向地址增大的方向增长
- 这个区域是在
运行时
使用的,由程序员
分配和释放内存,程序结束时操作系统会对其进行回收,(程序员分配内存像malloc、free、new、delete)都是在这个区域进行的; - 产生内存碎片:频繁的new/delete会造成内存空间不连续,产生大量碎片,使程序效率降低。栈不会产生碎片,因为栈是先进后出的队列。
- 空间大:一般来说,对于32位系统下,堆内存空间可以达到4G(2的32次方).从这个角度来看堆的空间是非常大的
- 堆的效率很低:堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
3.⛔️【注意】
无论堆栈都要防止产生越界,因为越界的结果很严重,要么程序崩溃,要么程序的堆栈结构被破坏,产生意想不到的结果。
三、指针的存储位置
在C++中,指针变量存放在内存的栈区
。栈区是一块连续的内存空间,用于存储局部变量和函数调用时的参数。当我们声明一个指针变量时,它会被分配在栈区,并且占用固定大小的内存空间。
指针变量存放在栈区中
,而指针所指向的对象或者内存地址则可以位于栈区、堆区或者全局/静态存储区。当我们使用指针来引用一个对象时,指针会保存该对象的内存地址。
需要注意的是,指针变量本身的值(即存放的地址)是存放在栈区的,而指针所指向的对象或者内存地址所对应的数据则可能位于其他不同的内存区域。
四、例题巩固
下面是考察C++内存的一道经典例题。
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
一、
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____ staticGlobalVar在哪里?____
staticVar在哪里?____ localVar在哪里?____
num1 在哪里?____
char2在哪里?____ *char2在哪里?___
pChar3在哪里?____ *pChar3在哪里?____
ptr1在哪里?____ *ptr1在哪里?____
解答:
1.globalVar在哪里?____
答:C数据段
。globalVar定义在全局,是一个全局变量。
2.staticGlobalVar在哪里?____
答:C.数据段
。该变量是static修饰的静态变量,存储于静态区。
3.staticVar在哪里?____
答:C.数据段
。该变量虽然处于函数体内,但是它是static修饰的静态变量,仍然存储于静态区。
4.localVar在哪里?____
答:A.栈
。该变量是一个普通的成员函数局部变量,存储于栈区。
5.num1 在哪里?____
答:A.栈
。该数组是属于普通的函数内的局部变量,依然存储在栈区。
6.🚩char2在哪里?____
答:A.栈
。char2是数组名。该数组是属于普通的函数内的局部变量,依然存储在栈区。
7.🚩*char2在哪里?___
答:A.栈
。*char2来说,很多同学就会认为它是在【常量区】中,实际上,数组名本身就是一个指针,指向这个字符串的首地址
。那我们对首元素地址
去进行解引用
的话就拿到了首字符的地址
,那么这也只是一个字符而已,并不是一个字符串,所以还是存放在【栈区】中的
8.pChar3在哪里?____
答:A.栈
。pChar3:是一个指针。指针存储在栈区。
9.*pChar3在哪里?____
答:D.代码段(常量区)
。第八题说过pChar3是一个指针,指向一串字符, 该字符存储于常量区,也就是代码段。
10.ptr1在哪里?____
答:A。栈
。ptr1仍然是一个指针,存在栈区。
11 . *ptr1在哪里?____
答:B.堆
。pttr1指向一块区域 ,对这块区域仅用也就是动态开辟的空间,存储在堆区。
五、类的成员函数和静态成员函数
一、公共代码区详解
之前讲过,类的成员函数并不占用类的内存空间,它们被存储在一个公共代码区。
问题1:公共代码区存储在哪里?
- 公共代码通常存储在特定的分区中,这个分区被称为第二分区。在这个分区中,公共代码是BOOTROM与软件程序共用的。除了公共代码之外,还有其他类型的代码,例如第一程序代码,它被存储在另一个称为第一分区的区域中。当需要运行软件升级时,系统会依据第一分区的起始地址来运行第一分区中的第一程序代码,然后调用第二分区中的公共代码,以接收并校验上位机发送的软件升级文件。如果校验成功,系统将利用软件升级文件来更新第三分区中记录的软件程序使用的除公共代码之外的第二程序代码。
请注意,这里的“分区”可能指的是计算机存储设备(如硬盘或闪存)上的逻辑分区,或者是内存中的不同区域,具体取决于上下文和系统的实现方式。同时,不同的系统或应用程序可能会有不同的分区方案和命名规则,因此具体的分区名称和用途可能会有所不同。
2.什么是第二分区?
在描述公共代码存储于“第二分区”时,这里的“第二分区”通常指的是存储设备(如硬盘、闪存等)上的一个逻辑区域,它被划分出来专门用于存储公共代码
。这里的“分区”是相对于整个存储设备而言的,它并不是指分区表中的某一个具体分区(如主分区、逻辑分区等),而是一个更为抽象的概念。
实际上,“第二分区”这个术语可能是特定于某个系统或应用程序的,用于描述存储设备上用于特定目的的一个区域。在不同的上下文或系统中,这个“第二分区”可能有不同的具体含义和用途。
如果您是在讨论某个具体的嵌入式系统、操作系统或应用程序中的分区方案,那么“第二分区”可能是该方案中定义的一个特定区域,用于存储公共代码、库函数、系统文件等共享资源。这个分区可能是由系统的启动加载器(BOOTROM、BIOS等)在启动时加载,并由运行时的程序或操作系统使用。
然而,在没有更多上下文信息的情况下,很难准确地说“第二分区”属于哪个具体的分区类型(如主分区、扩展分区、逻辑分区等),因为这取决于具体的分区方案和实现细节。如果您需要更详细的信息,建议查阅相关的系统文档、开发指南或分区表规范。
二、函数和函数代码存储位置
1.函数代码
都存储在代码段(文本段)。
- 无论是静态成员函数还是普通成员函数,它们的函数代码(指令)都存储在程序的代码段(也称为文本段或指令段)中,并在需要时被调用。代码段是只读的,并且包含程序执行时所需的所有指令。
2.静态成员函数和普通成员函数的主要区别在于它们对类成员变量的访问权限和调用方式,而不是它们在内存中的物理存储位置。
- 普通成员函数总是与类的实例(即对象)相关联。它们通常需要一个隐式的
this指针
来访问对象的非静态成员。 - 静态成员函数不与任何特定的类实例相关联。它们可以在没有创建类对象的情况下被直接使用
类名
去调用,因此不能直接访问类的非静态成员(变量或函数),除非显式地通过对象或指针来访问。 静态成员函数
只能直接访问静态成员变量或其他静态成员函数,而普通成员函数
可以访问所有类型的成员变量和函数。