C/C++内存管理-学习笔记

内存分配方式(三种)

从静态存储区域分配

内存在程序编译的时候就已经分配好了,在程序运行期间这块内存都存在,如全局变量,static变量等。

在栈上分配

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

在堆上分配

堆上内存分配(动态内存分配)在程序运行的时候使用malloc或new申请任意大小的内存,使用完后需要使用free或delete释放内存,动态内存的生存周期由我们决定,使用非常灵活,但是存在问题也多。(使用时需要包含malloc.h或stdlib.h头文件)

常见内存管理错误

内存分配未成功

由于内存空间有限,使用如动态内存分配方式分配内存空间时,有可能分配不成功。在使用内存之前检查指针是否为NULL,如使用assert(p != NULL)检查。如果使用malloc或new申请内存,应使用if (p != NULL)进行检查。

内存分配成功未初始化

内存空间在申请到后,其缺省值是未知的,所以在空间申请到后,不要嫌麻烦,即便是赋零值也是不可省略的。

操作内存越界

在进行循环操作时避免多加一或多减一的操作,避免操作超出分配空间的范围。

未释放内存导致内存泄漏

使用动态内存分配时,程序中malloc/free,new/delete必须配对使用。

释放内存继续使用

栈内存指针在函数返回后依旧调用或者使用free/delete释放动态内存后依旧使用。

内存管理基本规则

  • 判断内存释放分配成功:使用malloc或new申请内存之后,应该立即检查指针值是否为NULL,防止使用指针值为NULL的内存。
  • 不忘初始化:不要忘记为数组和动态内存赋初值。防止将未初始化的内存当有值使用。
  • 避免操作内存越界:避免数组或指针的下标越界。
  • 防止内存泄漏:动态内存的申请与释放必须配对,防止内存泄漏。
  • 防止野指针:使用fress或delete释放内存之后,理解将指针设置为NULL,防止产生”野指针”。

Sizeof与strlen对比

Sizeof可以求出数组容量,但是不能求出指针所指内存的容量。Sizeof指针只能求出指针变量的字节数。其中如果数组做函数的参数时,数组也是按指针处理的,即数组退化为指针:

Char x[] = “hello”

Char *p = x;

Sizeof(x) = 6;//包括字符串结束符’\0′

Sizeof(p) = 4;//与计算机位数有关

Void function(char x[10])

{

Sizeof(x) = 4;//与计算机位数有关

}

Strcpy-复制字符串

Strcmp-比较字符串

函数指针参数

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

举例:

//定义

Void Get_Memory(char *Point, int Depth)

{

P = (char *)malloc(sizeof(char) * Depth);

}

//调用

Void Test(void)

{

Char *Str = NULL;

Get_Memory(Str, 100);//d调用后Str依旧为NULL

Strcpy(Str,”hello”);//错误

}

说明:指针作为函数参数时,只能传入指针内存储的地址,不能在子函数中修改改值,但是能够修改该指针所指向的内存区间的值,其道理和一般的变量参数是一样的。函数参数在子函数中使用,但是不能对其进行修改,在子函数调用时只是将改参数值传递给了子函数定义时的变量,子函数执行过程中只是修改该变量,而不是调用是传递的参数。举例如下:

//定义

Void function(char x);

//调用

Char Y = 0;

Function(Y);

说明:在调用function函数时,Y作为参数将其值传递给了function定义时的参数X,在Function执行过程中只是对X进行操作,而不是对Y。

如果非要使用指针参数去申请内存,那么应该使用”指向指针的指针”,举例如下:

//定义

Void Get_Memory(char **Point, int Depth)

{

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

}

//调用

Void Test(void)

{

Char * Str = NULL;

Get_Memory(&Str, 100);//注意是&Str,是取了指针的地址,而不是指针指向的地址

Strcpy(Str, “hello”);

Free(Str);

}

说明:此方法中是使用指针的本身地址对其进行操作,修改了本身地址内的值,即将指针指向的地址值进行了修改。

函数返回值传递动态内存

举例说明:

//定义

Char *Get_Memory(int Depth)

{

Char *Point = (char *)malloc(sizeof(char) * num);

Return Point

}

//调用

Void Test(void)

{

Char * Str = NULL;

Str = Get_Memory(100);的地址

Strcpy(Str, “hello”);

Free(Str);

}

说明:以上方法比较方便,但是必须要保证malloc与free配对使用,避免内存溢出。在使用中定义需要使用malloc动态分配堆内内存,如果使用一下语句分配内存情况又不一样了,如下:

Char P[] = “Hello World”;//函数返回时P自动从栈内释放,所以函数返回报错

Char *P= “Hello World”;//常量字符串位于静态存储区,生命周期恒定不变,所以调用该子函数时,为”只读”状态,是一种错误的设计思想。

野指针问题

“野指针”不是NULL指针,是指向不明的指针。

“野指针”产生原因:

  • 指针变量未初始化:指针变量定义时指向是不明的,需要初始化。
  • 指针被Free/delete之后未设置为NULL:内存空间释放后指针变量并没被释放。
  • 指针操作越界:超出操作变量的范围。

Malloc/free与new/delete对比

Malloc/Free是C/C++的标准库函数

New/delete是C++的运算符

对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求,malloc/free是标准库函数,不是C++的运算符,不在编译器控制权限之内,不能够执行对象的构造函数和析构函数。

使用对比:

Malloc/free

1-malloc申请动态内存空间

2-初始化动态内存区

3-用户操作使用

4-清除工作

5-free释放内存空间

New/delete

1-new申请动态内存空间并初始化

2-用户操作使用

3-delete清楚并释放内存空间

由以上显而易见,不要使用malloc/free完成动态对象的内存管理,应该使用new/delete,但是对于内部数据类型两个是等价的。Malloc/free只是为了解决C语言动态内存分配的问题,在C++中既然有了new/delete,就不要为难自己使用前者了。

内存耗尽问题

如果动态申请分配的内存空间过大时将有可能导致内存分配不成功,即所谓的内存耗尽,使用malloc/new申请动态空间时将返回NULL指针。有如下两种处理方式:

  1. 判断指针是否为NULL,如果是则return或者exit(1)终止程序
  2. 为new和malloc设置异常处理函数

Visual c可以用_set_new_hander函数为new设置用户自己定义的异常处理函数,也可以让malloc享用与new相同的异常处理函数,需要参考相关手册了。

对于动态内存申请导致耗尽的问题必须要处理,不能留给操作系统处理。如果操作系统使用了”虚拟内存”,那么申请动态内存是不成问题的,操作系统会自动从硬盘中分配的虚拟内存中进行分配,所以这将导致硬盘压力很大,所以软件必须要对内存分配添加处理,不然程序质量将非常糟糕。

Malloc/free用法

函数原型:

Void *malloc(size_t size);//参数为字节数

Void free(void * memblock);

调用:

Int *P = (int *)malloc(sizeof(int) * Length);

Free(P);

说明

在使用malloc函数申请空间时需要注意参数是字节数,所以最好使用sizeof计算系统中数据类型暂用的字节数进行计算,不同位数的计算机对数据类型的字节数是不一样的。另外需要注意的是将void *类型转换为自己需要的数据指针类型,并且通过指针是否为NULL判断是否申请内存成功。

New/delete用法

调用:

//基本数据类型

Int * Ponit = new int[Length];

Delete [] Point;//[]不能少,表示此为数组空间,如果丢失的话会有Length-1个空间不能够被释放

//对象类型

Obj *Objects = new Obj;//无参数构造函数

Obj *Objects = new Obj[100];//创建100个无参数构造函数

Obj *Objects = new Obj(1);;//创建一个有参数的构造函数,并且初值为1,有参数的只能单独创建

说明:

New方法分配基本数据类型的内存空间时比较简单,但是针对动态对象类型需要根据对象的构造函数而定,无参数的构造函数操作和基本类型的操作近似,但是对于动态对象类型需要注意参数,并且只能单独创建,不能创建为数组。Delete释放空间时要根据申请的类型进行释放,不是简单的指针,不然会导致部分空间不能释放。

KEIL中实现内存管理

Keil中使用C语言,所以关于内存分配处理除了静态存储区域分配、在栈上创建和VC中的使用一样外,需要注意在堆上的分配(动态内存分配),在堆上分配内存需要使用malloc和free函数管理内存,这两个函数在stdlib.h标准库头文件中。

在keil 中使用malloc()函数经常会遇到不正常的情况,通常表现为不能正确分配内存空间,或者只能分配很小的空间。出现这个问题的原因大概有三个:

1、所用的ARM芯片本身内存已经被其代码占用,所余空间不够malloc分配。

解决办法:A、释放其他代码浪费的RAM空间;B、扩容。

2、未进行堆的初始化:

在KEIL中使用malloc函数时,必需要对heap进行初始化,否则不能正常使用malloc。

解决办法:A、使用KEIL自带的启动代码,该汇编启动代码本身已经完成了对heap的初始化;(我们通常建立的工程都是使用KEIL自带的启动代码,或者在该基础上修改的代码,所以这个问题基本上不用考虑);B、自己编写heap初始化汇编代码,该初始化代码必须放在调用C代码之前,最好放在启动代码中。

  • 3、堆空间太小。

解决办法:在堆初始化代码中,将堆大小增加,一般0x400大小足够,如果不够的话,可根据实际调试情况进行增加。

本文分享自微信公众号 - 黑光技术(helight_dt)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python爬虫与数据挖掘

Windows下安装Scrapy方法及常见安装问题总结——Scrapy安装教程

这几天,很多朋友在群里问Scrapy安装的问题,其实问题方面都差不多,今天小编给大家整理一下Scrapy的安装教程,希望日后其他的小伙伴在安装的时候不再六神无主...

8930
来自专栏腾讯技术工程官方号的专栏

大牛书单 | 春节特辑

? 导读:读书,伴随技术人的一生。技术人通过读书增长见闻、精进技术,提升人生境界。18年农历春节,我们特别邀请腾讯安全平台部总经理coolc、腾讯优图实验室主...

29380
来自专栏Python爬虫与数据挖掘

Windows下安装Scrapy方法及常见安装问题总结——Scrapy安装教程

这几天,很多朋友在群里问Scrapy安装的问题,其实问题方面都差不多,今天小编给大家整理一下Scrapy的安装教程,希望日后其他的小伙伴在安装的时候不再六神无主...

10320
来自专栏Soul Joy Hub

特定用户QQ群聊天记录导出的实现

这里要注意 : 导出之后的 文本是 unicode 编码的,需要转换 ==|| 之前不知道,搞了大半天。

15830
来自专栏TheOneGIS空间站

LeetCode-Longest Palindromic Subsequence

这是LeetCode的第516道题目:516. Longest Palindromic Subsequence。

12450
来自专栏TheOneGIS空间站

LeetCode-Palindromic Substrings

Given a string, your task is to count how many palindromic substrings in this st...

12170
来自专栏字根中文校对软件

C#:昨天,今天和明天:和 Anders Hejlsberg 座谈,第二部分

回到和语言相关的问题,我现在还是想说说LINQ。微软 Visual Stuido .NET 的产品经理 Tony Goodhew 在一次访谈中说过,微软的研究表...

12130
来自专栏CWIKIUS

Java 主要特性 原

在 Java 中,所有的都是对象。正式因为 Java 基于对象模型,所以 Java 更加容易进行扩展。

14940
来自专栏字根中文校对软件

google 和 unixlite 的设计理念

    昨天 feng.you 给我这条信息:the google legacy。   http://www.cincomsmalltalk.com/us...

9020
来自专栏大块小屋-技术

由浅入深学习单例模式

首先想到的是,利用局部static对象特性,产生全局唯一的对象。但是如何来避免更多的对象被实例化出来呢。

11570

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励