1、C&C++内存分布
C/C++中程序内存区域划分
- 内核空间(用户代码不可读写)
- 栈(向下生长):非静态局部变量/函数参数/返回值
- 内存映射段(文件映射、动态库、匿名映射):高效的I/O映射方式,用于装载一个共享的动态内存库。用户可以用系统接口创建共享内存,进行进程间通信
- 堆(向上增长):运行时动态内存分配
- 数据段(全局数据、静态数据):存储全局数据和静态数据
- 代码段(可执行代码/只读常量):可执行代码/只读常量
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
//变量存在数据段
int globalVar = 1;
static int staticGlobalVar = 1;
int main()
{
static int staticVar = 1; //变量存在数据段
//以下所有变量存在栈区
int localVar = 1;
int num1[10] = {1,2,3,4};
char char2[] = "abcd"; //值存在代码段
char *pchar3 = "abcd"; //值存在代码段
int *ptr1 = (int*)malloc(sizeof(int)*4); //值存在堆
int *ptr2 = (int*)calloc(4,sizeof(int)); //值存在堆
int *ptr3 = (int*)realloc(ptr2,sizeof(int)*4); //值存在堆
cout<<sizeof(num1)<<endl; //40
cout<<sizeof(char2)<<endl; //5
cout<<strlen(char2)<<endl; //4
cout<<sizeof(pchar3)<<endl; //8
cout<<strlen(pchar3)<<endl; //4
cout<<sizeof(ptr1)<<endl; //8
free(ptr1);
free(ptr3);
return 0;
}
2、C/C++动态内存管理方式
(1)C语言动态内存管理(malloc/calloc/realloc和free)
(2)C++内存管理方式(new/delete)
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
//动态申请一个int类型空间
int *ptr1 = new int;
//动态申请一个int类型的空间并初始化为10
int *ptr2 = new int(10);
//动态申请3个int类型的空间
int *ptr3 = new int[3];
delete ptr1;
delete ptr2;
delete[] ptr3;
return 0;
}
注:自定义类型操作与上操作一致。new会调用构造函数,delete会调用析构函数;而malloc/free不会。
3、operator new和operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new和delete在底层调用二者申请和释放空间。
operator new和operator delete实际也是通过malloc/free来申请和释放空间的。
4、new和delete的实现原理
(1)内置类型
new和malloc,delete和free基本类似,但:
- new/delete申请释放单个元素空间,new[]/delete[]申请释放连续空间
- new申请失败会抛出异常,malloc会返回NULL
(2)自定义类型
- new:调用operator new申请空间;在申请空间上执行构造函数,完成对象构造
- delete:在空间上执行析构函数,完成资源的清理工作;调用operator delete释放空间
- new[N]:调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请;在申请的空间上执行N次构造函数
- delete[]:在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理;调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
5、malloc/free和new/delete的区别与联系
(1)共同点:从堆上申请空间,且需要手动释放
(2)不同点:
6、两个例子
(1)设计一个类,要求只能在堆上创建对象
要点:构造函数私有化。构造函数和拷贝构造私有化,防止调用;提供静态成员函数,完成堆对象的创建。
(2)设计一个类,要求只能在栈上创建对象
要点:屏蔽new功能。屏蔽operator new。
/*
* 堆上创建对象:operator new
* 栈上创建对象:构造函数、拷贝构造
* */
class HeapOnly
{
public:
/*在静态成员函数完成堆对象的创建*/
static HeapOnly* CreateObj()
{
return new HeapOnly;
}
private:
/*防止调用构造函数或拷贝构造在栈上生成对象*/
HeapOnly()
{}
HeapOnly(const HeapOnly& heapOnly);
};
class StackOnly
{
public:
/*调用构造函数完成栈对象的创建*/
StackOnly()
{}
private:
/*屏蔽operator new防止在堆上生成对象*/
void* operator new(size_t size);
void operator delete(void* p);
};
7、内存泄漏
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
危害:长期运行的程序出现内存泄漏,会导致响应越来越慢,最终卡死。
原因:内存申请忘记释放;异常安全问题,即释放执行前有异常抛出导致未被释放。
避免:发生前,使用智能指针;发生后,使用泄漏检测工具。