《认清C++语言》之--内存管理

C++项目经验总结 1、C++的const比C语言#define更好的原因? 首先,它能够明确指定类型,有类型检查功能。 其次,可以使用C++的作用域规则将定义限制在特定的函数[常函数]或文件中。 第三,可以将const用于更复杂的类型,比如数组和结构。 C语言中也有const,在C语言中分配内存,其与C++中const的区别是:一是作用域规则不同;另一个是,在C++中可以用const值来声明数组长度。在C+ 阅读详情

内存分配方式主要有三种:

1)从静态存储区域分配;内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局变量、static变量;

2)在栈上创建;执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限;

3)从堆上分配,即动态内存分配。使用malloc或new申请,free或delete释放。

 

常见内存错误及其对策:

1)内存分配未成功,但使用了它。

常用的解决办法是:在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数入口处用assert(p!=NULL)进行检查;如果使用malloc或new申请的内存,应该用if(p==NULL)或if(p!=NULL)来防错。

 

2)内存分配虽然成功,但是尚未初始化就引用了它。

犯此类错误的原因:一是没有初始化的好习惯;二是误以为内存的缺省初值全为零,导致引用初值出错(例如数组)。内存的缺省初值是什么并没有统一的标准,因此,无论用何种方式创建数组,都应该赋初值,即使是赋全零。

 

3)内存分配成功且已经初始化,但操作越过了内存的边界。

最常见的就是数组下标操作和for循环语句。

 

4)忘记了释放内存,造成内存泄露。

 

5)释放了内存却继续使用它。

5.1)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或“引用”,因为该内存在函数体结束时被自动销毁了;

5.2)使用free或delete释放了内存后,没有将指针设置为NULL,导致产生“野指针”。

 

指针和数组的对比:

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变;

指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。

如下代码:

char a[] = "hello";

a[0] = 'X';    //可以改变

cout<<a<<endl;

char *p = "world";    //注意p指向常量字符串

p[0] = 'X';    //编译器不会发现该错误

cout<<p<<endl;

指针p指向常量字符串"world"(位于静态存储区,内容为world/0),而常量字符串的内容是不可以修改的,该语句企图修改常量字符串的内容而导致运行错误(编译没错)。

 

如下代码:

char a[] = "hello world";

char *p = a;

cout<<sizeof(a)<<endl;              //12字节

cout<<sizeof(p)<<endl;              //4字节

 

void Func(char a[100])

{

       cout<<sizeof(a)<<endl;              //4字节,而不是100字节

}

用运算符sizeof可以计算出数组的容量(字节数)。sizeof(a)的值是12(注意别忘了’/0’)。sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

 

使用指针参数传递内存:

如果函数的参数是一个指针,那么不要指望该指针去申请动态内存。

如下代码:

void GetMemory(char *p, int num)

{

       p = (char*)malloc(sizeof(char) * num);

}

 

void Test(void)

{

       char *str = NULL;

       GetMemory(str, 100);                     //str仍然为NULL

       strcpy(str, "hello");             //运行错误

}

问题出在GetMemory中,编译器总是要为函数的每个参数制作临时副本,指针参数p的副本设为_p,编译器是_p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改,这也是指针可以用作输出参数的原因。但是在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p却丝毫没变。因此,函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没用free释放申请的内存。

 

如果非要使用指针参数去申请内存时,应该改用“指向指针的指针“,如下代码:

void GetMemory(char **p, int num)

{

       *p = (char*)malloc(sizeof(char) * num);

}

 

void Test(void)

{

       char *str = NULL;

       GetMemory(&str, 100);                  //注意是&str,而不是str

       strcpy(str, "hello");

       cout<<str<<endl;

       free(str);

}

 

当然,我们也可以换一种方式,可以用函数返回值来传递动态内存(注意,就是堆上分配的),代码如下:

char* GetMemory(int num)

{

       char *p = (char*)malloc(sizeof(char) * num);

       return p;

}

 

void Test(void)

{

       char *str = NULL;

       str = GetMemory(100);

       strcpy(str, "hello");

       cout<<str<<endl;

       free(str);

}

用函数返回值来传递动态内存这种方法虽然好用,但常常有人把return语句用错啦!这里需要强调的是:用return语句返回的是在堆上分配的动态内存,而不是返回指向“栈内存“的指针,因为”栈内存”在函数结束时就自动消亡了!

如下代码:

char* GetString(void)

{

       char p[] = "hello world";

       return p;              //编译器将提出警告

}

 

void Test(void)

{

       char *str = NULL;

       str = GetString();              //str的内容不再是NULL,也不是”hello world”,而是垃圾

       cout<<str<<endl;

}

 

可以将上面代码改为:

char* GetString(void)

{

       char *p = "hello world";

       return p;      

}

 

void Test(void)

{

       char *str = NULL;

       str = GetString();

       cout<<str<<endl;

}

上面代码运行虽然不会出错,但函数GetString的设计理念是错误的!因为GetString内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内是恒定的,无论什么时候调用GetString,它返回的始终是同一个“只读”的内存块,那这个函数还有什么意义的,还不如设置一个常量算了。

 

free和delete:

free和delete只是把指针所指向德内存给释放掉,但并没有把指针本身销毁掉!指针(如p)被free以后它的地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p变成了“野指针”,如果此时不把p设置为NULL,会让人误以为p是个合法的指针。

如下代码:

char *p = (char*)malloc(100);

strcpy(p, "hello");

free(p);         //p所指的内存被释放了,但是p所指的地址仍然不变

....  

if(p != NULL)            //没有起到防错的作用

{

       strcpy(p, "world");            //出错

}

 

动态内存不会自动释放:

函数体内的局部变量在函数结束时自动消亡,但是如下代码:

void Func(void)

{

       char *p = (char*)malloc(100);       //动态内存不会自动释放

}

指针有一些似是而非的特征:

1)指针消亡了,并不表示它所指的内存会被自动释放;

2)内存被释放了,并不表示指针会消亡或者成为NULL指针。

 

杜绝野指针:

“野指针”不是NULL指针,而是指向“垃圾”内存的指针。

“野指针”的成因主要有两种:

1)指针变量没有初始化,任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应该被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

char *p = NULL;

char *str = (char*)malloc(100);

2)指针被free或delete之后,没有设置为NULL。

 

malloc/free和new/delete的区别:

1)malloc和free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存;

2)对于非内部数据类型的对象而言,光用malloc和free无法满足动态对象的要求。因为malloc和free是库函数而不是运算符,故不在编译器控制权限之内,不能把执行构造函数和析构函数的任务强加于malloc和free上;

3)因此,我们C++语言需要new和delete;注意,new和delete不是库函数。

4)如果用free释放new创建的动态对象,那么该对象因无法执行析构函数而可能导致程序出错;如果用delete释放malloc申请的动态内存,理论上程序不会出错,但程序可读性很差。

 

内存耗尽:

如果申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,通常有三种方式处理“内存耗尽”问题:

1)判断指针是否为NULL,如果是马上用return终止本函数:

void Func(void)

{

       A *a = new A;

       if(a == NULL)

       {

              return;

       }

       .....

}

2)判断指针是否为NULL,如果是马上调用exit(1)终止整个程序的运行:

void Func(void)

{

       A *a = new A;

       if(a == NULL)

       {

              cout<<"内存耗尽"<<endl;

              exit(1);

       }

       .....

}

3)为new和malloc设置异常处理函数。

 

Matlab GUIDE -- 串口操作 1、首先新建-->应用程序-->GUIDE。 2、绘制一个简单的UI。 3、然后右击“打开串口”图标-->查看回调-->callback,即可打开此图标的回调函数,此回调函数是press button时执行(可按需要选择其他回调)。 4、在回调函数中添加打开串口的代码。 delete(instrfindall); %删除所有串口 s3 = serial('CO 阅读详情

相关推荐

3D Slicer中Auto3DSeg和TotalSegmentator等自动分割插件总是安装失败,怎么办?教你两招

TotalSegmentator, MONAI auto3Dseg,这些工具因分割器官多,准确性高,备受关注。同时,这些自动分割工具被集成在了3D slicer平台,非常方便临床医生做手术规划。详细了解这些工具看之前的教程👇-------- 简单概述 --------其中,MONAIAuto3DSeg扩展附带数十种针对特定临床用例的预训练分割模型,这些模型旨在快速运行,几分钟内即可在任何普通计算机上运行,无需 GPU,甚至在笔记本电脑上。

zzjlhlcd的博客 1696

03 | 微服务反模式与陷阱:代码共享反模式

译者简介:ASCE1885, 《Android 高级进阶》作者。 本文首发于Source Code Chain开发者社区,欢迎使用我的专属邀请链接加入一起交流。 微服务被称为“无共享”架构,实际上,我更倾向于把它看作是一种“尽可能少共享”的架构,因为微服务之间不可避免的总会存在某种程度的代码共享。例如,与其使用一个负责身份验证和授权的安全服务,你可能会更倾向于将安全功能相关的代...

ASCE1885 4716

从科研小白到动物行为分析:我的DeepLabCut 3.0安装与初体验全记录

本文记录了从科研小白到成功安装并使用DeepLabCut 3.0进行动物行为分析的全过程。详细介绍了Anaconda环境配置、CUDA版本选择、DeepLabCut安装步骤及验证方法,并分享了初体验中的实用技巧和跨学科研究心得,为生物研究者提供了AI工具应用的实战指南。

weixin_30235225的博客 401

《认清C++语言》---new and delete

1)  new操作符(new operator)和operator new函数的区别: 下面的代码:string *ps = new string("Memory Management");使用的new是new操作符,这个操作符就像sizeof一样是内置于语言本身的。它所完成的功能分成两部分:首先分配足够的内存以便容纳所请求类型的对象(如上面的string对象);其次它会调用构造函

ASCE1885 1623

海思ISP调优实战:从实验室到真实场景的IPC相机图像质量优化指南

本文深入探讨海思ISP调优在IPC相机图像质量优化中的实战应用,从实验室标定到真实场景部署的全链路调优方法。重点解决光照动态范围、色温变化和噪声特性等核心问题,提供AE权重表配置、CCM标定、3DNR时域滤波等关键技术方案,帮助实现从理论到实践的图像质量提升。

s3t4u的博客 750

Github多人协作

最近有门课程需要几个人一起开发一个项目,此前我们基本都是单干的,多人合作意味着需要及时将我们每个人的代码进行合并,U盘传文件这种方式太慢了,效率不行,而且没法解决两个人同时修改一个文件的问题,这次我们就尝试使用Github来协助开发。下面就来记录一下这次开发遇到的一些问题和解决的方法。 Git的安装 从官网下载最新版按照默认安装即可。 此处不多加说明。 Git常用命令 在此放上一...

yzr1216的博客 1万+

《认清C++语言》---堆对象

强制在堆中建立对象:有时候,我们要求某种类型的对象能够自我销毁,也就是能够“delelte this”,因此,我们需要将此类型的对象分配在堆上。由于非堆对象在定义它的地方被自动构造,在生存期结束时被自动释放,因此我们只要禁止使用隐式的构造函数和析构函数就可以限制对象在堆上分配内存。 最直接的方法是将构造函数和析构函数都声明为private,但这样副作用太大。因此,最好将析构函数置为pr

ASCE1885 1739

基于STM32的浴室环境智能控制系统设计

摘要:本文设计了一款基于STM32F103的智能卫浴系统,通过多传感器协同检测浴室环境参数。系统采用DS18B20检测环境与卫浴温度,DHT11监测湿度,MQ-135检测空气质量,光敏电阻测量光照强度,D203S人体热释电模块检测人员存在。当检测到异常参数时,系统自动启动相应调节功能:温度异常时控制加热/制冷继电器,湿度过高时启动通风除湿,空气质量差时进行换气,光照不足且有人时自动开灯。系统支持按键、蓝牙APP和语音三种控制方式,可手动设置参数阈值、切换防雾模式、控制音乐播放等功能。实验结果表明,该系统能有

3254968421的博客 483

被误解的C++——C++的缺陷和D的缺陷

C++的缺陷和D的缺陷D语言,从字面上讲应当是在C/C++的基础上进了一位,其特性当然也进了一位。真是这样?也是,也不是。这得看你的出发点和价值观了。D的定位在于继承C/C++的优势,但却更加易学易用。这种定位招人喜爱。C/C++的致命伤就在难学难用上。(不少人认为C++难学易用,我也持这种观点。但是既然要拿来同D比较,那我也只能跳一回票。只此一次,下不为例)。我大致了解了一下D的特性

longshanks的专栏 6043

Unity 面试篇⚠️ | Unity性能优化好,山寨机都能跑~

Unity 面试篇⚠️ | Unity性能优化好,山寨机都能跑~

m0_58523831的博客 1万+

2025年全国青少年信息素养大赛初赛真题(算法创意实践挑战赛C++初中组:文末附答案)

2025年全国青少年信息素养大赛初赛真题(算法创意实践挑战赛C++初中组:文末附答案)

王老师青少年编程 3396

c语言深度剖析 视频,[C语言] C语言高手班 深度剖析视频教程 两期完整版

资源介绍C语言高手班 深度剖析视频教程推荐具有比较好的C语言基础的朋友再来进行学习,不是说初学者一定学不了但是在编程开发这类教程上面,还是一定要踏踏实实的才行!视频目录及截图:xa0xa0第10课 - 单引号和双引号.zipxa0xa0第11课 - 逻辑运算符使用分析.zipxa0xa0第12课 - 位运算符分析.zipxa0xa0第13课 - ++,--操作符使用分析.zipxa0xa0第14课...

weixin_39783915的博客 166

从零到一使用PXE实现系统的无人值守自动化安装的保姆级教程

本文是《从零到一使用PXE实现系统的无人值守自动化安装的保姆级教程》亲自实操可用,本文从PXE是什么?有啥用?及其亲自实践出通过以国产银河麒麟系统为例从零到一的搭建出PXE服务器端(适用于其他系统),且测试了该搭建的PXE服务端自动化无人值守安装系统的保姆级实操教程。适用于大批量设备系统安装部署场景。

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 580

C++内存管理与模板编程:从RAII智能指针到泛型设计的工程实践

在C++开发中,内存管理和代码复用是构建高性能、可维护系统的两大基石。内存管理涉及程序运行时资源的分配与释放,其核心原理是通过RAII(资源获取即初始化)机制,将资源生命周期与对象生命周期绑定,从而确保异常安全并避免内存泄漏。这一理念的技术价值在于实现了零开销的资源安全管控,是现代C++工程实践的必备技能。应用场景广泛,从简单的对象封装到复杂的资源管理器(如连接池、内存池)都需要依赖此机制。模板编程则提供了泛型设计能力,允许开发者编写与具体数据类型无关的通用代码,其原理是通过编译期代码生成实现类型安全的抽象

weixin_32306957的博客 333

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

十年C++企业级架构师分享:C++的核心知识点有哪些?

C++是一门被广泛使用的系统级编程语言,更是高性能后端标准开发语言;C++虽功能强大,灵活巧妙,但却属于易学难精的专家型语言,不仅新手难以驾驭,就是老司机也容易掉进各种陷阱。 这是小编准备的C++学习资料,加小编学习群:825414254,加群即可获取哦! 本文结合作者的工作经验和学习心得,对C++语言的一些高级特性,做了简单介绍;对一些常见的误解,做了解释澄清;对比较容易犯错的地方,做了归纳总...

2482

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

c++编程规范的纲要和记录

这是一本好书, 可以让你认清自己对C++的掌握程度.看完之后,给自己打分,我对C++了解多少? 答案是不足20分.对于我自己是理所当然的问题, 就不提了, 记一些有启发的条目和细节: (*号表示不能完全理解,实力升级了之后回头看) 一般性问题: 1. 不要在注释中重复写代码语义,这样很容易产生不一致. 应该编写的是解释方法和原理的说明性注释. 2. 不要对每个项目每个文件进行强制的排版格式规定,...

程序员之路-Stay Hungry. Stay Foolish. 23

浩鲸科技C++校招备战指南:从语言基础到多线程核心考点

C++作为系统级编程语言,因其对内存的精细控制和高性能特性,在通信、后端服务等底层架构中占据核心地位。理解指针、引用与内存管理的原理,掌握RAII与智能指针机制,是构建稳定代码的基石。多线程与并发控制则直接关系到高并发场景下的系统可靠性,而STL容器与算法的底层实现,更能体现工程师对工程性能的深刻洞察。从快速幂到单调栈,针对性的算法训练能有效提升笔试竞争力。结合vscode环境搭建与调试实践,这些知识共同构成了C++开发者的核心能力体系。本文以浩鲸科技C++校招为背景,系统梳理了从语言细节到工程素养的完整备

weixin_31155097的博客 303

C++ new 的三种面貌

C++ 中使用 new 运算符产生一个存在于 Heap(堆)上的对象时,实际上调用了 operator new() 函数和 placement new() 函数。

Doublelv 的博客专栏。 4717

python的内存回收机制原理_python的内存管理和垃圾回收机制详解

如何理解和掌握Python垃圾回收机制现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。 对于一个字符串、列表、类甚至数值都你是小编患得患失的梦,小编是你可有可无的人。毕竟这穿越山河的箭,刺的都是用情至疾的人。Python里的垃圾回收机制是什么意...

weixin_33705384的博客 734

到底什么资格,才算真正的资深 C++ 开发专家

本文系统剖析了资深C++专家的核心能力标准,指出行业普遍存在"伪资深"现象。真正资深专家需具备八大核心资质:精通C++语法体系与标准演进、STL源码级掌握、编译链接原理、内存精细管理、高性能并发编程、底层组件开发、崩溃排查调优能力以及架构设计决策力。文章详细拆解了各资质的技术要点,包括现代C++特性、容器底层实现、内存池设计、无锁编程等硬核内容,并提供了量化评估标准。强调C++开发者必须从功能实现转向底层掌控,在高性能复杂项目中持续打磨,才能突破职业瓶颈。全文为C++从业者提供了清晰的高

分享知识,拥抱变化 584

国嵌c语言深度,国嵌C语言3部全

课程目录:国嵌 C语言深度剖析班:1课-基本数据类型分析2 auto,register,static分析3 if,switch,do,while,for分析4 goto,void,extern,sizeof分析5 const和volatile分析6 struct和union分析7课-enum和typedef分析8 注释符号9 接续符和转义符10 单引号和双引号11 逻辑运算符使用分析12 位运算符...

weixin_42123237的博客 314
上一篇: 线段树
下一篇: 线段树的代码模板
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值