转自:https://blog.csdn.net/ling_hun_pang_zi/article/details/81746543
C/C++动态内存管理
内存分配的方式有3种:
1、从静态存储区域分配
内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如:全局变量、static变量。
2、在栈上分配
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。效率很高,但是分配的内存容量有限,可能出现栈溢出。
3、从堆上分配(动态内存分配)
程序在运行时候用malloc()或new等申请任意的内存,程序员自己负责在何时用free()或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
C 动态内存管理
C语言使用malloc/calloc/realloc/free进行动态内存管理
malloc函数
void *malloc(size_t size);
功能:允许从空闲内存池中分配连续内存但不初始化。
参数:size参数是一个所需字节数的整数
返回:若分配成功则返回一个指向该内存块的指针,在使用时可根据需要做强制类型转换,否则返回NULL(空指针)
calloc函数
void *calloc(size_t num, size_t size);
功能:同malloc,但作初始化。
参数:num是所需元素的数量,size是每个元素的字节数。
返回:同malloc函数
realloc函数
void *realloc(void *ptr, size_t new_size);
功能:在指针ptr指向的内存基础上扩大或缩小内存
参数:ptr是指向先前通过malloc、calloc、和realloc函数后分配的内存块的指针,new_size是内存块的新尺寸,可能大于或小于原有内存尺寸。
返回:若能调整内存大小,则返回指向调整后内存的指针,否则返回NULL。
realloc函数的使用注意点
当扩展内存时,不会对添加进内存块的字节进行初始化
若不能调整内存则返回NULL,但原有内存中的数据不会发生改变。
若第一个参数为NULL,那么功能等同于malloc函数,若第二个参数为0,那么会释放调用内存块。
在缩小或扩大内存时一般不会对其移动,若无法扩大内存块(内存块后面的字节已经用于其它目的),那么可能会在别的地方分配新的内存块,然后把旧的内存块中的数据复制到新的内存块中,并将旧的内存块删除释放。
free函数
void free(void *ptr);
功能:释放由指针ptr指向的动态分配的内存块
注意
如果ptr是NULL指针,那么free对ptr无论操作多少次都不会有问题。
如果ptr不是NULL指针,那么free对ptr连续操作两次就会导致程序运行错误。
int *p1 = (int*)malloc(sizeof(int)* 4);
int *p2 = (int*)calloc(4, sizeof(int));
int *p3 = (int*)realloc(p2, sizeof(int)* 4);
C++动态内存管理
C++通过new和delete动态管理对象
new/delete动态管理对象
new[]/delete[]动态管理对象数组
有了malloc/free为什么还要new/delete?
malloc()与free()是C/C++语言的标准库函数,new/delete是C++的运算符。它们都可用于申请和释放内存。
对于自定义类型而言,malloc()/free()无法满足动态对象的要求:对象在创建的同时要自动调用构造函数,对象在销毁时要自动调用析构函数。
因此,C++语言需要一个能够完成动态内存分配和初始化工作的运算符new,以及一个能够完成清理与释放内存的运算符delete。
从内存池申请一些内存需要用new,它将根据你提供的数据类型分配一块大小适当的内存。
如果有足够的可用内存,new语句将返回新分配地址块的起始地址。
如果没有足够的可用内存空间,就会抛出std::bad_aloc异常。
注意:在用完内存块之后,应该用delete语句释放。另外附加一种保险的措施,在释放了内存块之后,还应该把与之关联的指针设置为NULL。
用new运算符实现动态内存分配
int* p1 = new int; // 动态分配 4 个字节( 1 个 int)的空间单个数据
int* p2 = new int(10); // 动态分配 4 个字节( 1 个 int)的空间并初始化为 10
int* p3 = new int[10]; // 动态分配 40 个字节( 10 个 int)的空间
例:
class AA
{
public:
AA()
{
cout << "AA()" << endl;
}
~AA()
{
cout << "~AA()" << endl;
}
private:
int _a;
};
int main()
{
AA* p1 = new AA[10];
delete[] p1; //为什么知道析构多少次?
//因为多开了4个字节的空间
}
int main()
{
AA* p0 = new AA;
//free(p0); //不会崩, 未调析构 可能会内存泄漏
delete[] p0; //会崩,会越界 误以为多开了4个字节的空间
return 0;
}
int main()
{
AA* p1 = new AA[10];
//free(p1);//会崩 释放错位置
//delete p1; //会崩 释放错位置 在屏蔽了自定义类型里写的构造和析构后 又不会崩
//严格来说 new[]一定要多开4个字节,但编译器会优化,编译器识别到析构函数可调可不调的
时候,就不用多开4个字节
//要是自己写了析构,则编译器一定会调,不匹配就会出错
delete[] p1;
}
int main()
{
//这种情况内置类型不匹配不会崩
int* p2 = new int[40]; //不会多开4个字节
delete p2; // 编译器优化 编译器识别到析构函数可调可不调的时候 就不用多开4个字节
return 0;
}
有关operator new/operator delete operator new[]/operator delete[]
总结:
operator new/operator delete operator new[]/operator delete[]的用法和malloc/free一样。
它们只负责分配空间/释放空间,不会调用对象构造函数和析构函数来初始化/清理对象。
实际operator new/operator delete 只是malloc和free的一层封装。
malloc()/free()和new/delete的区别和联系
它们都是动态管理内存的入口。
malloc/free是C/C++标准库函数,new/delete是C++操作符。
malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化和清理工作。
malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型的大小,返回对应类型的指针。
malloc失败返回NULL,new失败抛异常。
常见的内存错误及避免
1、使用未分配成功的内存
在使用内存之前检查指针是否为NULL
2、引用分配成功但尚未初始化的内存
赋初值,即便是赋零值也不可省略
3、内存分配成功并且已经初始化,但操作越过了内存的边界
注意下标的使用不能超出边界
4、忘记释放内存,造成内存泄露
动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同(new与delete同理),否则会出现问题。
5、释放了内存却继续使用它
使用free或delete释放了内存后,将指针设置为NULL。若没有设置为NULL,就会产生“野指针”。
内存错误的注意点
指针消亡了,但并不表示它所值的内存被自动释放。
内存被释放了,并不表示指针会消亡或者为NULL指针。
野指针
野指针的形成是指针变量没有被初始化,任何指针变量刚被创建时不会自动称为NULL指针,它的缺省是随机的。
指针变量在创建的同时应该被初始化,要么将指针设置为NULL,要么让它指向合法内存。
使用free或delete释放了内存后,将指针设置为NULL。若没有设置为NULL,就会产生“野指针”。