Lesson05--C/C++内存管理

news2024/9/23 16:22:59
  • 1. C/C++内存分布
  • 2. C语言中动态内存管理方式
  • 3. C++中动态内存管理
  • 4. operator newoperator delete函数
  • 5. newdelete的实现原理
  • 6. 定位new表达式(placement-new)
  • 7. 常见面试题

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在哪里?__(1)__ staticGlobalVar在哪里?__(2)__
staticVar在哪里?__(3)__ localVar在哪里?__(4)__
num1 在哪里?__(5)__

 1.C:全局变量都在静态区

 2.C:

3.C:用static修饰的局部变量也在静态区

4.A:局部变量都在栈上        

5.A

char2 在哪里? ____ * char2 在哪里? ___
pChar3 在哪里? ____ * pChar3 在哪里? ____

ptr1在哪里?____ *ptr1在哪里?____  

1. A:char2还是一个数组它就在栈上

2.A:*char2,数组名解引用结果是数组的第一个,所以还是在栈上

3.A:pchar3是一个指针类型所以也是在栈上

4.D:pChar它解引用是"abcd",通过解引用以后也无法对原来的数据进行修改,"abcd"本质就是代码段

5.A:也是一个指针类型,所以在栈上

6.B:malloc的都是在堆上

2. 填空题:

sizeof ( num1 ) = ____ ;
sizeof ( char2 ) = ____ ; strlen ( char2 ) = ____ ;
sizeof ( pChar3 ) = ____ ; strlen ( pChar3 ) = ____ ;
sizeof ( ptr1 ) = ____ ;

 sizeof(num1) = 40

sizeof数组名是整个数组的大小

sizeof(char2) =5

四个字符以外还有'\0',也是一个字符

 strlen(char2)=4

sizeof(pChar3) = 8,32位下就是4,这里是一个指针的大小

strlen(pChar3) = 4

sizeof(ptr1) = 4

3. sizeof strlen 区别?

  • sizeof 用于获取类型或对象的内存大小,包括字符串结束符。
  • strlen 用于获取字符串的实际字符长度,不包括结束符。

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 的区别?
malloc不会初始化
calloc会初始化成0
2. malloc的实现原理?
【CTF】GLibc堆利用入门-机制介绍_哔哩哔哩_bilibili

3. C++内存管理方式  

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

3.1 new/delete操作内置类型 

int main()
{
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;

	free(p1);
	delete p2;
}

 这俩种写法是等价的

int main()
{
	int* p1 = new int;
	int* p2 = new int[10];

	delete p1;
	delete[] p2;

}

多个的这样写就行

还可以进行初始化

 单个的用()括号进行初始化,多个的用{}用来初始化,没有初始化的部分全部初始化成0

 

 注意:申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[]

delete[] ,注意:匹配起来使用。

3.2 newdelete操作自定义类型  

class A
{
public:
	A(int a=0,int b=0)
	{
		_a = a;
		_b = b;
	}

private:
	int _a;
	int _b;
};

int main()
{
	A* p1 = new A;
	A* p2 = (A*)malloc(sizeof(A));
}

 

可以看见一个会掉构造函数一个不会

甚至new的还可以用初始化列表的初始化方法来初始化

 类的成员变量都是私有的那么在想去初始化就不行了

 创建多个的话每一个都会调用构造函数和析构函数

多参数用{}就可以了

// new/delete malloc/free 最大区别是 new/delete 对于【自定义类型】除了开空间还会调用构
造函数和析构函数
注意:在申请自定义类型的空间时, new 会调用构造函数, delete 会调用析构函数,而 malloc free 不会

4. operator newoperator delete函数(重要点进行讲解)

 4.1 operator newoperator delete函数(重点)

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

底层代码看不懂也没事

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
    return (p);
}
/*
    operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
    _CrtMemBlockHeader * pHead;
    RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)
    return;
    _mlock(_HEAP_LOCK); /* block other threads */
    __TRY
    /* get a pointer to memory block header */
    pHead = pHdr(pUserData);
    /* verify block type */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    _free_dbg( pUserData, pHead->nBlockUse );
    __FINALLY
    _munlock(_HEAP_LOCK); /* release other threads */
    __END_TRY_FINALLY
    return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

 new是操作符,malloc是函数,new底层其实就是封装malloc,new会调用构造函数,会抛出异常

5. newdelete的实现原理 

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 来释放空间

通过汇编可以看到这里用了44字节的内存,10个整形应该是40字节啊,多的4的字节拿去干嘛了?

多的4的字节拿来保存10了,这里是为了析构函数

 delete[]里面就是缺省的上面的10,这里就会调用10次析构函数

 

 改变参数,可以发现这里跟着改变了

 delete[]向前偏移数,这里多的4的字节就是对象个数,方便delete去,知道有多少个对象去调用析构函数

这里可以得出一个结论,malloc就和free配套使用,new就和delete使用,要不然可能会有各种各样的问题

6. 定位new表达式(placement-new) (了解)  

定位 new 表达式是在 已分配的原始内存空间中调用构造函数初始化一个对象
使用格式:
new (place_address) type 或者 new (place_address) type(initializer-list)
place_address 必须是一个指针, initializer-list 是类型的初始化列表
使用场景:
定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new 的定义表达式进行显示调构造函数进行初始化。

class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}

7. 常见面试题

7.1 malloc/freenew/delete的区别  

malloc/free new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
  • 1. mallocfree是函数,newdelete是操作符
  • 2. malloc申请的空间不会初始化,new可以初始化 
  • 3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个 对象,[]中指定对象个数即可
  •  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  • 5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常 
  • 6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间 后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构(邓俊辉)学习笔记】优先级队列 08——左式堆:结构

文章目录 1. 第一印象2. 堆之合并3. 奇中求正4. NPL5. 左倾性6. 左展右敛 1. 第一印象 在学习过常规的完全二叉堆之后&#xff0c;我们再来学习优先级队列的另一变种&#xff0c;也就是左式堆。所谓的左式堆&#xff0c;也就是在拓扑形态上更加倾向于向左侧倾斜的一种堆&#…

Vue3 项目结构

1.main.ts 2.简单写一个src下的结构 App.vue 根组件 <template><div class"app"><!-- html --><h1>你好啊!</h1></div> </template><script lang"ts"> //js 或 tsexport default {name:App,//组件名 }…

MySQL数据库锁机制(全面讲解)

目录 1、全局锁 1.1、全局锁使用语法 1.2、备份数据库&#xff08;不使用全局锁&#xff09; 2、表锁 2.1、读写锁 读锁 写锁 2.2、元数据锁&#xff08;meta data lock MDL&#xff09; 2.3、意向锁 3、行锁 3.1、共享锁和排他锁 共享锁&#xff08;S锁&#xff…

原来marker还能这么玩

在Web GIS开发中&#xff0c;Marker&#xff08;标记&#xff09;是一个基本但强大的工具。今天&#xff0c;我们将探讨如何通过不同的API调用&#xff0c;将Marker玩出新花样&#xff0c;让地图更加生动有趣。 最基础的Marker用法是在地图上标记一个具体位置。我们可以通过“m…

【Unity3D小技巧】Unity3D中实现对InputField的自定义输入限制实例

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 InputField是UGUI的文本输入框&#xff0c;自带的…

粘包,Telnet,SSH,Wireshark

一&#xff0c;粘包 原因&#xff1a;tcp为流式套接字&#xff0c;数据与数据间没有边界&#xff0c;导致多次数据粘到一起。 解决&#xff1a; 1.规定一些数据间的间隔符&#xff0c;"\aa","\r\n"; 2.可以指定要发送对象的数据长度。 3.自己将数据打包。 …

力扣:有效的数独

文章目录 需求分析结尾 需求 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08…

2024年最新最全的【大模型学习路线规划】从零基础入门到精通!

2024年最新最全的大模型学习路线规划&#xff0c;对于零基础入门到精通的学习者来说&#xff0c;可以遵循以下阶段进行&#xff1a; 文章目录 一、基础准备阶段数学基础&#xff1a;编程语言&#xff1a;深度学习基础&#xff1a; 二、核心技术学习阶段Transformer模型&#xf…

第2章-04-Request Header与Response Header

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年CSDN全站百大博主。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于专栏:Web爬虫入门与实战精讲,后续完整更新内容如下。 文章…

TortoiseGit使用教程

系列文章目录 文章目录 系列文章目录前言一、TortoiseGit下载安装二、使用步骤1.创建库1.需要上传的文件&#xff1a;不需要上传的文件&#xff1a; 2.拉取代码 前言 以前都是用svn&#xff0c;现在改用git Git是目前世界上最先进的分布式版本控制系统&#xff08;没有之一&am…

IO进程day01(标准IO、缓存区)

目录 【1】标准IO 1》概念&#xff1a; 2》特点 【2】缓存区 1》全缓存&#xff1a;和文件相关 2》行缓存&#xff1a;和终端有关 3》不缓存&#xff1a;也就是没有缓存区&#xff0c;标准错误。 【1】标准IO 1》概念&#xff1a; 标准IO&#xff1a; 是在C库中定义的一…

Linux入门——10 信号

1.信号 1.信号------信号量&#xff08;两者没有任何关系&#xff09; 2.信号讲什么----->整个信号的生命周期 信号的产生-----信号的保存------信号的处理 之前的kill命令&#xff0c;用的就是信号。 kill -l查看系统支持的信号 名字本身就是宏&#xff0c;其实就是编…

java—1 封装

目录 一、零碎内容 一、输入、输出 二、idea项目结构 三、Java标识符的命名规范&#xff08;约定俗成&#xff09; 四. 方法和函数 二、快捷键 三、数组 1. 声明数组 2. 静态初始化 3. 数组动态初始化 4. 声明和初始化一起完成 5. 数组地址 四、面向对象编程 1. …

进程函数练习

创建子父进程&#xff0c;子进程将1.txt内容拷贝到2.txt中&#xff0c;父进程将3.txt内容拷贝到4.txt中。 #include <myhead.h>int main(int argc, const char *argv[]) {pid_t ID;ID fork();if(ID>0)//父进程{printf("父进程ID:%d\n",ID);int fd open(&…

C HTML格式解析与生成

cmake报错替换 if(NOT MyHTML_BUILD_WITHOUT_THREADS OR NOT MyCORE_BUILD_WITHOUT_THREADS) set(CMAKE_THREAD_PREFER_PTHREAD 1) if (WIN32) set(CMAKE_USE_WIN32_THREADS_INIT ON) set(CMAKE_THREAD_PREFER_PTHREADS TRUE) set(THREADS_PR…

Covalent Network(CXT)第二季度委托激励增长83%

Covalent Network&#xff08;CXT&#xff09;是一家领先的区块链数据解决方案供应商&#xff0c;拥有超过 230 条链的集成和数十亿个结构化数据点&#xff0c;专注于去中心化应用&#xff08;dApps&#xff09;和长期数据可用性。Covalent Network&#xff08;CXT&#xff09;…

C语言 之 整数在内存中的存储、大小端字节序和字节序的判断

文章目录 整数在内存中的存储大小端字节序和字节序判断大小端有大小端的原因高位和地位怎么区分&#xff1f;图例判断机器大端还是小端的例题 整数在内存中的存储 整数的2进制表示方法有三种&#xff0c;即 原码、反码和补码 三种表示方法均有符号位和数值位两部分&#xff0c…

DHCP DNS 欺骗武器化——实用指南

DHCP 枚举 在我们之前的文章中,我们分享了 DHCP DNS 欺骗背后的理论。实际上,需要几条信息才能有效地执行我们描述的攻击。对于攻击者来说幸运的是,发现DHCP 服务器并了解其配置的能力是 DHCP 协议的一部分,这使得侦察过程变得微不足道。 在以下章节中,我们将描述攻击者…

基于华为昇腾910B和LLaMA Factory多卡微调的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…

PostgreSQL 与对象存储的结合: 在 MinIO 中访问外部数据

数据领域最激动人心的发展之一是湖仓一体功能在所有主要数据库供应商中的兴起。Snowflake 和 SQL Server 长期以来一直采用这一点&#xff0c;现在 PostgreSQL 正在通过 pg_lakehouse 拥抱这种范式转变&#xff0c;使得利用现代数据湖进行分析、AI 等比以往任何时候都更容易。随…