深入浅出之动态内存(new,malloc深度分析)
void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } 请问运行Test函数会有什么样的结果? 答:程序崩溃。 因为GetMemory并不能传递动态内存, Test函数中的 str一直都是 NULL。 strcpy(str, "hello world");将使程序崩溃。 |
char *GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); } 请问运行Test函数会有什么样的结果? 答:可能是乱码。 因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。 |
void GetMemory2(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 请问运行Test函数会有什么样的结果? 答: (1)能够输出hello (2)内存泄漏 |
void Test(void) { char *str = (char *) malloc(100); strcpy(str, “hello”); free(str); if(str != NULL) { strcpy(str, “world”); printf(str); } } 请问运行Test函数会有什么样的结果? 答:篡改动态内存区的内容,后果难以预料,非常危险。 因为free(str);之后,str成为野指针, if(str != NULL)语句不起作用。 |
1. 关键字、操作符与库函数
- 关键字是编译器保留的文字,不能被用户拿来重新声明,像const, new, if等等
- 操作符必须要有操作对象,操作符本质上可以视为编译器内置的基础的函数。操作符在c++中,可以被重载(除了部分例外,比如 . :: sizeof)。
- 库函数是编写在编译器头文件库里,要包含头文件才能调用的封装函数。
2. 自由存储区与堆的区别
堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:
堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
3. 野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
4. malloc和new区别
在C++中,申请动态内存与释放动态内存用new/delete 与 malloc/free都可以,new/malloc申请动态内存,操作系统无法自动回收,需要对应的delete/free释放空间。
- malloc/free 是c语言中的库函数,需要头文件支持;而new/delete 是c++中的关键字
- 使用new操作符时编译器会根据类型信息自行计算所需要的内存,而malloc需要显式指出所需内存的尺寸
int *p = new int[2];
int *q = (int *)malloc(2*sizeof(int));
malloc内存分配成功时,返回的是void*,需要转换成所需要的类型
new内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符
free释放内存的时候需要的是void* 类型的参数
delete释放内存的时候需要使用具体类型的指针
new操作符在分配失败的时候会抛出bac_alloc异常
malloc在分配内存失败时返回NULL
new操作符从自由存储区(free store)上为对象动态分配内存空间,允许重载new/delete操作符
malloc函数从堆上动态分配内存,malloc不允许被重载
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
5. new()和new[]的区别
- new()创建一个对象
- new[]创建一个动态数组
- 释放方法也不一样,new()对应delete,new[]对应delete[]
char *pc = new char('a'); //开辟一个内存单元,并用括号里的初始化
char *pca = new char[15]; //开辟一个数组
释放内存的方法也不一样:
delete pc;
delete []pc;
6. delete和delete[]的区别
new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[],其实要分基本数据类型和自定义数据类型。
基本数据类型
int *a = new int[10];...delete a; // 方式1delete [ ] a; //方式2
基本的数据类型对象没有析构函数,并且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。
- 自定义数据类型
#include <iostream>;
using namespace std;class T {
public:T() { cout << "constructor" << endl; }~T() { cout << "destructor" << endl; }
};int main()
{const int NUM = 3;T* p1 = new T[NUM];cout << hex << p1 << endl; //输出P1的地址// delete[] p1;delete p1;cout << endl;T* p2 = new T[NUM];cout << p2 << endl; //输出P2的地址delete[] p2;return 0;
}
从运行结果中我们可以看出,delete p1 在回收空间的过程中,只有 p1[0] 这个对象调用了析构函数,其它对象如 p1[1]、p1[2] 等都没有调用自身的析构函数,这就是问题的症结所在。如果用 delete[],则在回收空间之前所有对象都会首先调用自己的析构函数。
基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。
所以一个简单的使用原则就是:new 和 delete、new[] 和 delete[] 对应使用。
7. delete或者free之要将指针指为NULL
delete和free被调用后,内存不会立即回收,指针也不会指向空,delete或free仅仅是告诉操作系统,这一块内存被释放了,可以用作其他用途。但是由于没有重新对这块内存进行写操作,所以内存中的变量数值并没有发生变化,这时候就会出现野指针的情况。因此,释放完内存后,应该把指针指向NULL
#include <iostream>
using namespace std;int main()
{int *p = new int;*p = 3;cout << *p << endl;cout << p << endl;delete p;cout << *p << endl;cout << p << endl;return 0;
}
delete之后,p指向的地址并没有发生变化,而地址里面的内容却成了随机数,那么,我们可以得到下面这条结论
我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身。
此时p也就成为一个野指针。
#include <iostream>
using namespace std;int main()
{int *p = new int;delete p;p = NULL;delete p;
}
如果不加p=NULL;程序运行时就会挂掉,加上就不会了,对NULL空间多次释放时没有问题的,但是不是NULL就不可以,因为这段空间已经释放掉,不属于本程序,不能够取随意的释放。
8. 使用原则
- 原则1: 优先使用new,delete
- 原则2: 要new和delete配对,new[]和delete [],malloc和free配对使用
- 原则3: free和delte后指针一定赋予NULL,防止成为野指针
9.C++智能指针如何指向数组
智能指针在帮助C++程序员管理动态内存方面可谓神兵利器,但是在有些情况下我们想要对数组进行动态内存管理就会发现一个问题 咦?shared_ptr 在默认情况下是不能指向数组的,那是为什么呢。
原因是因为我们的 shared_ptr 默认的删除器是使用 Delete 对智能指针中的对象进行删除,而 delete 要求 new 时是单一指针 Delete时也应该是指针 new时是数组 delete 也应该用数组类型去delete
shared_ptr
所以我们如果想让我们的 share_ptr 去指向指针 我们只需要去使用一个可调用对象即可 在这种情况下比较常用的函数或者lambda表达式均可
bool del(int *p){delete [] p;
}shared_ptr<int> shared(new int[100],del);//使用函数shared_ptr<int> ptr(new int[100],
[](int *p){delete [] p;});//使用lambda表达式
因为智能指针没有重载下标运算符 意味着我们不能想数组那样去使用这个指针 那怎么样才可以使用呢
shared_ptr 有一个函数可以返回当前智能指针的内置指针的函数 就是成员函数get() 但是我们要注意当智能指针指针已经释放内存以后,get得到的指针就成了空悬指针
有一点需要注意 在我们得到get返回的指针以后,智能指针对象的引用计数其实并没有增加
get() const noexcept{ return _M_ptr; }
这是get的函数定义
但是如果我们delete从get得到的指针 并不会出现多次delete的错误,现在还不是很理解为什么
我们该如何使用从get中得到的指针呢 其实很简单,就是我们一般的指针操作即可
auto x = ptr.get();cout << *(x+i) << endl;
注意!!!
其实只是C++11 中不支持而已 C++17中已经支持
unique_ptr
相比与shared_ptr unique_ptr对于动态数组的管理就轻松多了 我们只需要直接使用即可
unique_ptr<int[]>unique(new int[100]);
而且unique_ptr是重载了下标运算符的,意味着我们可以方便把其当数组一样使用
Boost C++库
著名的Boost库其实是支持指向数组的,使用方法与unique_ptr差不多
Boost库是什么?
类名为boost::shared_array,定义在<boost/shared_ptr.hpp>
boost::shared_array<int> arr(new int[100]);
参考文献:
- 关键字、操作符与库函数01
- 自由存储区和堆的区别
- new 和 malloc free 和 delete 的区别
- C++中的delete和delete[ ]的区别
- C++中delete和delete[]的区别 - charley_yang - 博客园
- C/C++动态内存管理malloc/new、free/delete的异同
- delete一个指针之后,要记得设置为NULL
- delete或者free之后为什么要将指针指为NULL
C++智能指针如何指向数组
深入浅出之动态内存(new,malloc深度分析)相关推荐
- c语言malloc引用类型作参数,c语言中动态内存分配malloc只在堆中分配一片内存.doc...
c语言中动态内存分配malloc只在堆中分配一片内存 .C语言中动态内存分配(malloc)只在堆中分配一片内存,返回一个void指针(分配失败则返回0),并没有创建一个对象.使用时需要强制转换成恰当 ...
- 关于动态内存分配malloc的初级用法和注意事项
#include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv[]) { /*your c ...
- linux c free大段内存,Linux C 动态内存分配--malloc,new,free及相关内容
一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...
- C语言学习笔记10-指针(动态内存分配malloc/calloc、realloc、释放free,可变数组实现;Tips:返回指针的函数使用本地变量有风险!;最后:函数指针)
C语言:指针 1. 指针:保存地址的变量 *p (pointer) ,这种变量的值是内存的地址. 取地址符& 只用于获取变量(有地址的东西)的地址:scanf函数-取地址符 地址的大小 ...
- C语言-动态内存管理(malloc()、calloc()、realloc()、free())
C语言 动态内存分配 文章目录 C语言 动态内存分配 前言 一.为什么存在动态内存分配? 二.动态内存函数的介绍 1.初识malloc()和free() 2.malloc()和free()的简单使用 ...
- 动态内存函数malloc和free
1.malloc函数 malloc是用来申请动态内存的函数: malloc声明如下: void* malloc (size_t size); malloc向内存申请一块连续可用的空间,并返回指向这块空 ...
- 动态内存管理 - malloc、calloc、realloc、柔性数组
目录 一.为什么存在动态内存分配 二.动态内存函数的介绍 1.1 malloc 1.2 free 1) 动态开辟多少个字节的内存空间,返回该空间的起始地址:且开辟的空间使用方法,类似于数组,是一块连续 ...
- 动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。
我们知道内存的使用方式,可以在栈区,静态区,堆区,申请空间来储存变量. 但是他们这些内存区所存储的东西是不一样的. 局部变量 函数的形式参数 栈区 动态内存分配申请的空间 malloc,calloc, ...
- C和指针之动态内存分配malloc、calloc、realloc简单使用和区别
1.介绍 3个函数都在<stdlib.h>头文件 1).void* malloc(unsigned size); 在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需 ...
最新文章
- 利用startup.m文件设置matlab的Current Folder的默认文件夹
- 3种用python代码实现99乘法表
- html for 循环模板
- 基于分层注意力网络的方面情感分析
- 【Python3网络爬虫开发实战】1.4.2-MongoDB安装
- rhel7 mysql无法运行_redhat7下mysql5.7.12重启电脑后起不来问题
- uni-app中的样式
- OpenCV : 投影变换
- python制作一个简易计算器_最简易的python计算器实现源代码
- 三菱plc 毕设_软件分享 | 学习PLC,这个PLC仿真软件你需要知道
- IP协议号与传输层端口
- 面试官:谈谈你对geohash的理解和如何实现附近人功能呢?
- 国内最牛的Java面试八股,不接受反驳。
- CSS Core CSS hack CSS优化
- 当x大于时,y等于1;当x等于0时,y等于0;当x小于0时,y等于-1。
- Python--变量
- 雅思写作6.5分的奥秘在这里
- 全球3D打印机行业一流服务品牌
- 文件上传漏洞靶场upload-labs学习(pass1-pass5)
- 手机的像素密度是怎么来的?
热门文章
- wxWidgets:wxCalculateLayoutEvent类用法
- boost::mpl模块实现front相关的测试程序
- boost::EccentricityProperty用法的测试程序
- 基于Boost::beast模块的单元测试程序
- Boost:标准地图比较的测试程序
- DCMTK:Telnet发起程序(ti)主程序
- VTK:可视化之ColorGlyphs
- VTK:PolyData之PointSource
- VTK:Rendering之ColoredSphere
- OpenCV安全栅栏摄像头security barrier camera的实例(附完整代码)