动态内存管理
- C语言动态内存管理
C语言的动态内存分配通过系统提供的库函数来实现,主要有:malloc,calloc,realloc,free这四个函数。
①malloc
函数原型:
void* malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。函数返回所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置。如果分配不成功,返回NULL。
int* p1 = (int*)malloc(sizeof(int)*4);
②calloc
函数原型:
void* calloc(unsigned n, unsigned size);
其作用是在内存的动态存储区分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。函数返回指向分配域的起始位置的指针。如果分配不成功,返回NULL。
int* p2 = (int*)calloc(6, sizeof(int));
③realloc
函数原型:
void* realloc(void*p, unsigned int size);
int* p3 = (int*)realloc(p2, sizeof(int)*6);
④free
函数原型:
void free(void* p);
其作用是释放指针变量p指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc或realloc函数时得到的函数返回值。
free(p1);
free(p3);
- C++动态内存管理
C++通过new和delete(操作符)动态管理内存
new / delete动态管理对象
new[] / delete[]动态管理对象数组
void Test()
{
int* p4 = new int; //动态分配4个字节(1个int)的空间单个数据
int* p5 = new int(3); //动态分配4个字节(1个int)的空间并初始化为3
int* p6 = new int[3]; //动态分配12个字节(3个int)的空间
delete p4;
delete p5;
delete[] p6;
}
int有构造函数,带一个参数,但int不能重载。
int main()
{
int i = 10;
cout << i << endl; //10
i = int();
cout << i << endl; //0
i = int(20);
cout << i << endl; //20
system("pause");
return 0;
}
内存泄漏
内存泄漏
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。
内存管理
全局变量:可以被本程序所有对象或函数引用
局部变量:只能被函数内部引用,而无法被其它对象或函数引用
全局静态变量:是在全局变量前加一个static,使该变量只在这个源文件中可用
局部静态变量:通常放在函数内部,只能在函数内部,只进行一次初始化,每次执行函数时保持上一次执行时的值
C语言中的地址都是虚拟地址原因如下:
使不同程序的地址空间相互隔离
如果所有程序都直接使用物理内存,那么程序所使用的地址空间不是相互隔离的。恶意程序可以很容易改写其他程序的内存数据,以达到破坏的目的;有些非恶意、但是有 Bug 的程序也可能会不小心修改其他程序的数据,导致其他程序崩溃。
提高内存使用效率
使用虚拟地址后,操作系统会更多地介入到内存管理工作中,这使得控制内存权限成为可能。例如,我们希望保存数据的内存没有执行权限,保存代码的内存没有修改权限,操作系统占用的内存普通程序没有读取权限等。
new[]时底层处理的机制
class AA
{
public:
AA(int a = 10, int b = 20)
:_a(a)
, _b(b)
{
cout << "AA(int a = 10, int b = 20)" << endl;
}
~AA()
{
cout << "~AA()" << endl;
}
private:
int _a;
int _b;
};
int main()
{
AA* p1 = new AA;
delete p1;
system("pause");
return 0;
}
new 会先开辟空间,再调用构造函数
delete 会先调用析构函数,再释放空间
在使用库函数或操作符进行动态内存开辟时,一定要匹配使用,若不匹配可能会造成内存泄漏。
这样写程序虽然可以正常执行,但很明显在释放内存时并未调用析构函数进行清理。如果在其构造函数内再动态开辟一段空间,那么用free去释放空间则会造成内存泄漏。
AA* p2 = new AA[10];
delete p2; //程序崩溃
free(p2); //程序崩溃
delete[] p2;
结论:
严格来说,new[]一定要多开四个字节,但当构造和析构屏蔽时,析构可调可不调时,编译器会优化,不需多开四个字节。
• operator new / operator delete :
operator new / operator delete并不是new / delete的重载,其用法由与malloc / free一样
它们只负责分配空间 / 释放空间,不会调用对象构造函数 / 析构函数来初始化 / 清理对象
实际上operator new / operator delete只是malloc / free的一层封装
AA* p3 = (AA*)operator new(4); //未调用构造与析构
operator delete (p3);
AA* p4 = (AA*)operator new[](sizeof(AA)*10);
operator delete[] (p4);
new/delete和malloc/free的区别
- 它们都是动态管理内存的入口
- new/delete是C++操作符,malloc/free是C/C++准库函数
- malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员 )
- malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型大小,返回对应类型指针