C++学习————第八天(C/C++内存管理)

devtools/2024/11/13 10:12:28/

目录

1、1.C/C++内存分布

2、 C语言中动态内存管理方式:malloc/calloc/realloc/free

3、C++内存管理方式

3.1 new/delete操作内置类型

3.2 new和delete操作自定义类型

4.operator new与operator delete函数

5. new和delete的实现原理

5.1 内置类型   

5.2 自定义类型

>)delete注意事项

1、1.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在哪里?__C__  staticGlobalVar在哪里?__C__staticVar在哪里?__C__  localVar在哪里?__A__num1 在哪里?__A__分析:globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区staticVar静态局部变量在静态区  localVar局部变量在栈区num1局部变量在栈区char2在哪里?__A__  *char2在哪里?__A__pChar3在哪里?__A__   *pChar3在哪里?__D__ptr1在哪里?__A__    *ptr1在哪里?__B__分析:char2局部变量在栈区  char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上pChar3局部变量在栈区   *pChar3得到的是字符串常量字符在代码段ptr1局部变量在栈区     *ptr1得到的是动态申请空间的数据

【说明】

1、栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2、内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(现在只需要了解, 后面在linux中会提到)
3、堆用于程序运行时动态内存分配,堆是可以上增长的。
4、数据段–存储全局数据和静态数据。
5、代码段–可执行的代码/只读常量。


2、 C语言中动态内存管理方式:malloc/calloc/realloc/free


void Test()
{int* p1 = (int*)malloc(sizeof(int));free(p1);// 1.malloc/calloc/realloc的区别是什么?int* p2 = (int*)calloc(4, sizeof(int));int* p3 = (int*)realloc(p2, sizeof(int) * 10);// 这里需要free(p2)吗?free(p3);
}

1. malloc/calloc/realloc的区别?

三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。

(1)malloc函数。其原型void *malloc(unsigned int num_bytes);
              num_byte为要申请的空间大小,需要我们手动的去计算,如int *p = (int*)

malloc( 20*sizeof(int)),如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请一个80Byte的连续空间,并将空间基地址强制转换为int类型,赋值给指针p,此时申请的内存值是不确定的。

(2)calloc函数,其原型void *calloc(size_t n, size_t size);
        其比malloc函数多一个参数,并不需要人为的计算空间的大小,比如如果他要申请20个int类型空间,会int *p = (int *)calloc(20, sizeof(int)),这样就省去了人为空间计算的麻烦。但这并不是他们之间最重要的区别,malloc申请后空间的值是随机的,并没有进行初始化,而calloc却在申请后,对空间逐一进行初始化,并设置值为0;

既然calloc不需要计算空间并且可以直接初始化内存避免错误,那为什么不直接使用calloc函数,那要malloc要什么用呢?
实际上,任何事物都有两面性,有好的一面,必然存在不好的地方。这就是效率。calloc函数由于给每一个空间都要初始化值,那必然效率较malloc要低,并且现实世界,很多情况的空间申请是不需要初始值的,这也就是为什么许多初学者更多的接触malloc函数的原因。

(3)realloc函数和上面两个有本质的区别,其原型void realloc(void *ptr, size_t new_Size)
用于对动态内存进行扩容(及已申请的动态空间不够使用,需要进行空间扩容操作,ptr为指向原来空间基址的指针, new_size为接下来需要扩充容量的大小。


3、C++内存管理方式

       C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

3.1 new/delete操作内置类型

void Test()
{//内置类型// 管理对象// 动态申请一个int类型的空间int* ptr4 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);//管理对象数组// 动态申请10个int类型的空间int* ptr6 = new int[3];//动态申请10个int类型的空间,并初始化为1 2 3 4int* ptr7 = new int[10] {1, 2, 3, 4}; //后面的会默认初始化为0//释放一个对象delete ptr4; delete ptr5;//释放多个对象delete[] ptr6; delete[] ptr7;
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。

3.2 new和delete操作自定义类型

       new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数

class A
{
public:A(int a = 0): _a(a){//cout << "A():" << this << endl;cout << "A():" << endl;}A(int a1, int a2){cout << "A(int a1, int a2)" << endl;}A(const A& aa): _a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){//cout << "~A()" << this << endl;cout << "~A()" << endl;}
private:int _a;
};int main()
{// new/delete 和 malloc/free最大区别是// new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(2);free(p1);delete p2;// 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;free(p3);delete p4;A* p5 = (A*)malloc(sizeof(A) * 10);A* p6 = new A[10]; //调用10次构造函数free(p5);delete[] p6; //调用10次析造函数//多个对象A aa1(1);A aa2(2);A aa3(3);A* p7 = new A[10]{ aa1,aa2,aa3 };delete[]p7;// 多参数A* p7 = new A[10]{ 1,2,3,4,5,{6,7}};//用1生成临时对象,然后被编译器合二为一(隐式对象转换)delete[]p7;return 0;
}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会

4.operator new与operator delete函数

1.new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数
2.new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局 函数来释放空间。

5. new和delete的实现原理

5.1 内置类型   

  如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

5.2 自定义类型

new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请
2. 在申请的空间上执行N次构造函数
delete[]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间

总结:new == operator new -> (malloc) + 构造函数
           ~~~~~~~~~~          delete == 析构函数 + operator delete -> (free)
           ~~~~~~~~~~          特别的:new失败了,抛异常, 不需要再检查返回值

>)delete注意事项

ClassA *pclassa=new ClassA[5];
delete pclassa;

析构函数此时只会调用1次,要想完整释放数组空间,需要使用[]
注意:程序可能崩溃


http://www.ppmy.cn/devtools/6469.html

相关文章

AI系列:大语言模型的function calling(下)- 使用LangChain

目录 前言LangChain Tool/Function calling1. Tool/function加强功能LangChain的tool装饰器其他方式: Pydantic 2. 绑定tools/functions3. 调用大模型(LLM)4. function calling处理流程 LangChain版代码与原生LLM调用的比较参考 前言 在AI系列&#xff1a;大语言模型的functio…

esp32s3中使用双通道通信解决TCP粘包问题

在使用esp32 idf例程中的tcp_server和tcp_client通信测试时发现&#xff0c; 在tcp_server端&#xff0c;接收到一帧数据之后必须马上回复至少一个字节&#xff0c;才能保证每帧数据不粘包&#xff0c; 如果不回复操作&#xff0c;300ms以内的通信时延会导致tcp严重粘包&…

Android AIDL接口

一.AlDI接口简介 AIDL&#xff08;Android Interface Definition Language&#xff09;是一种 IDL 语言&#xff0c;用于生成可以在 Android 设备上两个进程之间进行进程间通信&#xff08;IPC&#xff09;的代码。 通过 AIDL&#xff0c;可以在一个进程中获取另一个进程的数据…

XiaodiSec day028 Learn Note 小迪渗透学习笔记

XiaodiSec day028 Learn Note 小迪渗透学习笔记 记录得比较凌乱&#xff0c;不尽详细 day 28 还是 sql 注入 知识点 提交方式的注入 在 php, spring boot, flask 都有相关的提交方式 提交方式的注入 数据以某种方式提交到后端 数据大小和数据类型和提交方式有关 如身份…

uniapp之消除图片的空白占用空间

我们在使用uniapp开发的过程中一定会遇到一个情况就是我们加载的图片总有一点空白出现在不该出现的地方代码如下 <view style"background:#ff0000;"><image style"width:100%;"src"https://t7.baidu.com/it/u1819248061,230866778&fm19…

安卓广播发送接收流程

本文基于Andorid 11。 一、registerReceiver registerReceiver(new MyRecevier(), new IntentFilter("com.example.broadcast"));动态注册广播接收器&#xff0c;参数&#xff1a;BroadcastReceiver, IntentFilter。 <receiver android:name".MyReceiver&…

ubuntu在xshell中使用快捷方式操作命令,减少命令行的数入量

第一步 第二步 然后无脑确定 第三步 在xshell的显示方式 方式一 这样就会在每个窗格中进行显示 方式二 效果显示–> 这种窗格的显示是全局的 然后你双击这个process就会自动把命令打在命令行上&#xff0c;减少你的输入量

基于改进遗传算法的配电网故障定位(matlab代码)

1 主要内容 该程序复现文章《基于改进遗传算法的配电网故障定位》&#xff0c;将改进的遗传算法应用于配电网故障定位中, 并引入分级处理思想, 利用配电网呈辐射状的特点, 首先把整个配电网划分为主干支路和若干独立区域, 再利用该算法分别对各独立区域进行故障定位, 然后进行…