当前位置: 首页 > news >正文

C/C++之内存旋律:星辰大海的指挥家

 

个人主页:日刷百题

系列专栏〖C/C++小游戏〗〖Linux〗〖数据结构〗 〖C语言〗

🌎欢迎各位点赞👍+收藏⭐️+留言📝 

一、C/C++内存分布

我们先来了解一下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.1 内存划分

 1、选择题:

选项:     A.栈                 B.堆             C.数据段(静态区)                D.代码段(常量区)

  • globalVar在哪里?C
  • staticGlobalVar在哪里?C
  • staticVar在哪里?C
  • localVar在哪里?A
  • num1 在哪里?A

char2在哪里?A

  • 字符串在常量区,char2会栈上开辟一个数组,然后将常量区的字符串拷贝过到栈上,所以char2是栈上的指针变量,上题的num1是直接在栈上开辟的。

*char2在哪里?A

  • char指向栈上开辟的数组的指针,*char解引用表示栈上开辟空间的内容

pChar3在哪里?A

  • const修饰的是代表常变量,不代表就在常量区,pChar3是栈上的一个指针变量,指向了常量区的字符串

*pChar3在哪里?D

  • pChar3指向了常量区的字符串,解引用就是常量区的字符串

ptr1在哪里?A

  • 也是栈上的一块指针变量

*ptr1在哪里?B

  • ptr1指向开辟在堆上的空间,解引用表示堆空间的内容

内存区域划分:

【说明】

1.栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2.内存映射段是高效的 I/O 映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(未学到linux仅作了解即可)

3.用于程序运行时动态内存分配,堆是可以上增长的。

4.数据段--存储全局数据和静态数据。

5.代码段--可执行的代码/只读常量。


2.1 sizeof 和 strlen 区别

在C/C++中:

sizeof:这是一个运算符,编译时确定,用于计算变量或类型的大小(以字节为单位),包括数组、指针、结构体等。对于字符数组或字符串,它返回整个数组(包括结束符 \0)的总字节数。

strlen:这是一个库函数,运行时确定,用于计算以 \0 结尾的字符串的实际字符数,不包括结束符 \0。因此,对于包含字符串的字符数组,strlen 返回的是字符串的有效字符数量。

总结:

sizeof 计算内存容量。
strlen 计算字符串长度。

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);
}
  • sizeof(num1) = 40;
  • sizeof(char2) = 5;
  • strlen(char2) = 4;
  • sizeof(pChar3) = 4/8;
  • strlen(pChar3) = 4;
  • sizeof(ptr1) = 4/8;

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

#include<stdio.h>
#include<malloc.h>
int main()
{int* p1 = (int*)malloc(sizeof(int));free(p1);int* p2 = (int*)calloc(4, sizeof(int));// 这里需要free(p2)吗?//free(p2);int* p3 = (int*)realloc(p2, sizeof(int) * 10);free(p3);
}
  • 为什么不需要free(p2)?

        因为p2申请了四个字节的空间,此基础上,如果free掉p2,那么p3申请空间的时候,p2指向那块空间已经属于操作系统了,这时候再操作就引发野指针异常


  •  malloc/calloc/realloc的区别?

malloc:

  1. 功能:动态分配指定字节数的内存。
  2. 特点:不会初始化分配的内存,内容随机。
  3. 语法:void* malloc(size_t size);
  4. 示例:int *p = (int*)malloc(sizeof(int)*n); 分配n个整数大小的连续内存空间。

calloc:

  1. 功能:动态分配指定数量、特定类型的内存空间,并初始化为0。
  2. 特点:不仅分配内存,还会清零初始化。
  3. 语法:void* calloc(size_t num, size_t size);
  4. 示例:int *p = (int*)calloc(n, sizeof(int)); 分配并初始化n个int大小的连续内存空间为0。

realloc:

  1. 功能:调整已分配内存块的大小,可以扩大或缩小。
  2. 特点:如果扩大内存,新增空间内容不确定;如果缩小内存,多余部分会被释放,缩小后的内存区域保持不变。
  3. 语法:void* realloc(void* ptr, size_t new_size);
  4. 示例:int *new_p = (int*)realloc(p, sizeof(int)*m); 尝试更改指针p指向的内存区域大小为m个整数所需空间,返回新的内存地址,有可能与原地址相同也可能不同。

总的来说:

malloc 用于单纯分配未初始化的内存。
calloc 用于分配并初始化为零的内存。
realloc 用于调整已分配内存区域的大小,提供了一种灵活的内存管理手段。

   

三、C++内存管理方式

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因

此C++又提出了自己的内存管理方式:通过 new和delete 操作符进行动态内存管理

3.1 new/delete操作内置类型

  •  malloc和new对于内置类型都只会申请空间但不会初始化 ,因为 new没有调构造函数

  • 同样对于delete和free来说,对于内置类型都只会释放对象的空间, delete不会调用析构函数

int main()
{// 管理对象// 动态开辟一个int类型的空间int* p1 = new int;// 动态开辟一个int类型的空间,并初始化为1int* p2 = new int(1);//管理对象数组// 动态开辟一个int类型的数组int* p3 = new int[10];// 动态开辟一个int类型的数组,并初始化int* p4 = new int[10]{};// 不写初始化值,默认初始化为 0// 动态开辟一个int类型的数组,并初始化int* p5 = new int[10]{ 1,2,3 };// 前三个分别初始化为1 2 3,后面默认初始化为 0//释放开辟的内存delete p1;delete p2;delete[] p3;// 对应释放数组加[]delete[] p4;delete[] p5;return 0;
}

 注意:申请和释放单个元素的空间,使用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;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);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];free(p5);delete[] p6;return 0;
}

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


四、new和delete的实现原理
 

newdelete是用户进行动态内存申请和释放的操作符,(new先开辟空间,再调用构造函数,delete先调用析构函数,再释放空间)。operator newoperator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

4.1 operator new与operator delete函数

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid *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 new对malloc 的封装,失败抛异常实现new

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_FINALLYreturn;
}
  • free本质是一个宏,也是调用_free_dbg这个函数,举例如下:
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

总结:

operator new是对malloc进行封装,operator delete是对free进行封装(类似对比:引用的底层原理也是指针实现的)。

4.2  new和delete原理总结

3.2.1 内置类型
  • 如果申请的是内置类型的空间,new和malloc,delete和free基本类似
  • 不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间
  • new在申请空间失败时会抛异常,malloc会返回NULL。
3.2.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表达式(placement-new)

  • 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
  • 使用格式:
  1. new (place_address) type或者new (place_address) type(initializer-list)
  2. place_address必须是一个指针,initializer-list是类型的初始化列表
// 定位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;
}

定位new是使用在一种特殊场景——内存池

因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显式调构造函数进行初始化。


五、malloc/free和new/delete的区别

共同点:

  • 都是从堆上申请空间,并且需要用户手动释放。

不同点:

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

相关文章:

C/C++之内存旋律:星辰大海的指挥家

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 一、C/C内存分布 我们先来了解一下C/C内存分配的几个区域&#xff0c;以下面的代码为例来看…...

Linux下进程的调度与切换

&#x1f30e;进程的调度与切换 文章目录&#xff1a; 进程的调度与切换 进程切换 进程调度       活动状态进程队列       位图判断       过期队列 总结 前言&#xff1a; 在Linux操作系统中&#xff0c;进程的调度与切换是操作系统核心功能之一&#xff…...

Linux相关命令(2)

1、W &#xff1a;主要是查看当前登录的用户 在上面这个截图里面呢&#xff0c; 第一列 user &#xff0c;代表登录的用户&#xff0c; 第二列&#xff0c; tty 代表用户登录的终端号&#xff0c;因为在 linux 中并不是只有一个终端的&#xff0c; pts/2 代表是图形界面的第…...

React中 类组件 与 函数组件 的区别

类组件 与 函数组件 的区别 1. 类组件2. 函数组件HookuseStateuseEffectuseCallbackuseMemouseContextuseRef 3. 函数组件与类组件的区别3.1 表面差异3.2 最大不同原因 1. 类组件 在React中&#xff0c;类组件就是基于ES6语法&#xff0c;通过继承 React.component 得到的组件…...

GPT实战系列-智谱GLM-4的模型调用

GPT实战系列-智谱GLM-4的模型调用 GPT专栏文章&#xff1a; GPT实战系列-实战Qwen通义千问在Cuda 1224G部署方案_通义千问 ptuning-CSDN博客 GPT实战系列-ChatGLM3本地部署CUDA111080Ti显卡24G实战方案 GPT实战系列-Baichuan2本地化部署实战方案 GPT实战系列-让CodeGeeX2帮…...

AndroidStudio开发 相关依赖

1、com.google.zxing 用于二维码扫描 2、butterknife 用于简化findView 和 onClick操作 3、pub.devrel:easypermissions 简化权限请求的库 4、 网络请求框架(一)&#xff1a;android-async-http 网络请求框架(二)&#xff1a;xUtils 网络请求框架(三)&#xff1a;Volley Volley…...

Zookeeper详解(zk)

文章目录 Zookeeper 概念ZooKeeper的应用场景使用场景zk的原理ZooKeeper、Nacos、Eureka 和 Consul区别Zookeeper的数据结构zk集群脑裂如何解决ZAB 协议假如注册中心挂了&#xff0c;消费者还能调⽤服务吗&#xff0c;用什么调用的dubbo注册中心为什么选择 Zookeeper关于zookee…...

BSD-3-Clause是一种开源软件许可协议

BSD-3-Clause是一种开源软件许可协议&#xff0c;也称为BSD三条款许可证。它是BSD许可证家族中的一种&#xff0c;是一种宽松的许可证&#xff0c;允许软件自由使用、修改和重新分发&#xff0c;同时也保留了一些版权和责任方面的规定。 BSD-3-Clause许可证的主要特点包括以下…...

持续集成平台 02 jenkins plugin 插件

拓展阅读 Devops-01-devops 是什么&#xff1f; Devops-02-Jpom 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 代码质量管理 SonarQube-01-入门介绍 项目管理平台-01-jira 入门介绍 缺陷跟踪管理系统&#xff0c;为针对缺陷管理、任务追踪和项目管理的商业…...

LoadBalancerCacheManager not available, returning delegate without caching

警告&#xff1a;LoadBalancerCacheManager not available, returning delegate without caching 背景&#xff1a;更换了redis集群 解决方案&#xff1a; 重启gateway网关服务 也就是重启引用下面这个包的服务 <dependency><groupId>org.springframework.cloud…...

机器学习金融应用技术指南

1 范围 本文件提供了金融业开展机器学习应用涉及的体系框架、计算资源、数据资源、机器学习引擎、机 器学习服务、安全管理、内控管理等方面的建议。 本文件适用于开展机器学习金融应用的金融机构、技术服务商、第三方安全评估机构等。 2 规范性引用文件 下列文件中的内容通过…...

ES6生成器(Generator)

一、function* 概念简介&#xff1a;function* - JavaScript | MDN (mozilla.org) function* 声明创建一个绑定到给定名称的新生成器函数。生成器函数可以退出&#xff0c;并在稍后重新进入&#xff0c;其上下文&#xff08;变量绑定&#xff09;会在重新进入时保存。 1.1 y…...

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 并训练自己的数据集

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 概述 大模型微调(finetuning)以适应特定任务是一个复杂且计算密集型的过程。本文训练测试主要是基于主流的的微调方法:LoRA、Adapter、Prefix-tuning、P-tuning和Prompt-tuning,并对…...

【No.13】蓝桥杯二分查找|整数二分|实数二分|跳石头|M次方根|分巧克力(C++)

二分查找算法 知识点 二分查找原理讲解在单调递增序列 a 中查找 x 或 x 的后继在单调递增序列 a 中查找 x 或 x 的前驱 二分查找算法讲解 枚举查找即顺序查找&#xff0c; 实现原理是逐个比较数组 a[0:n-1] 中的元素&#xff0c;直到找到元素 x 或搜索整个数组后确定 x 不在…...

【蓝桥杯-单片机】基于定时器的倒计时程序设计

基于定时器的倒计时程序 题目如下所示&#xff1a; 实现过程中遇到的一些问题 01 如何改变Seg_Buf数组的值数码管总是一致地显示0 1 2 3 4 5 首先这个问题不是在main.c中关于数码管显示部分的逻辑错误&#xff0c;就是发生在数码管的底层错误。 检查了逻辑部分&#xff…...

QT:三大特性

QT的三大特性&#xff1a; 1、信号与槽 2、内存管理 3、事件处理 1、信号与槽 当信号产生时&#xff0c;就会自动调用绑定的槽函数。 自定义信号: 类中需要添加O_OBJECT宏 声明: signals标签之下进行声明 定义&#xff1a; 信号不需要定义 …...

无服务器推理在大语言模型中的未来

服务器无服务器推理的未来&#xff1a;大型语言模型 摘要 随着大型语言模型(LLM)如GPT-4和PaLM的进步&#xff0c;自然语言任务的能力得到了显著提升。LLM被广泛应用于聊天机器人、搜索引擎和编程助手等场景。然而&#xff0c;由于LLM对GPU和内存的巨大需求&#xff0c;其在规…...

【linux】CentOS查看系统信息

一、查看版本号 在CentOS中&#xff0c;可以通过多种方法来查看版本号。以下是几种常用的方法&#xff1a; 使用cat命令查看/etc/centos-release文件&#xff1a; CentOS的版本信息存储在/etc/centos-release文件中。可以使用cat命令来显示该文件的内容&#xff0c;从而获得C…...

部署dagu_1.12.10+replicadb0.15.1+sqlline1.12

下载所需的文件并上传到服务器 dagu_1.12.10(选择适合自己的版本): https://gh.api.99988866.xyz/https://github.com/dagu-dev/dagu/releases/download/v1.12.10/dagu_1.12.10_linux_amd64.tar.gzreplicadb0.15.1(包含了sqlline1.12): https://download.csdn.net/download/u0…...

基于Java中的SSM框架实现社会保险管理系统项目【项目源码+论文说明】

摘要 改革开放的浪潮已近深深的键入人心&#xff0c;随之而带来了我国社会主义市场经济体制的不断完善和建立健全&#xff0c;作为国家基本保障之一的社会保险系统&#xff0c;我们不单单要把握社保金额的进展和状态&#xff0c;还需要对社会保障基金开展全面、科学、可持续的…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...