C/C++内存管理

发布于:2025-06-19 ⋅ 阅读:(13) ⋅ 点赞:(0)

1. C/C++内存分布

程序内存分为以下区域(从低地址到高地址):

  1. 代码段:存放可执行代码和只读常量(如"abcd"字符串常量)。
  2. 数据段(静态区):存储全局变量(globalVar)、静态变量(staticGlobalVarstaticVar)。
  3. :动态分配的内存(malloc/new申请的空间),向上增长
  4. 内存映射段:文件映射、动态库等。
  5. :非静态局部变量(localVarnum1)、函数参数等,函数结束即销毁,向下增长
int globalVar = 1;//全局变量,存储在数据段
static int staticGlobalVar = 1;//全局变量+静态变量,存储在数据段

void Test()
{
	static int staticVar = 1;//静态变量,存储在数据段

	//存储非静态局部变量,函数结束后就销毁,存储在栈上
	int localVar = 1;//非静态局部变量,存储在栈上
	int num1[10] = { 1, 2, 3, 4 };
	char char2[] = "abcd";//“abcd”是只读常量,存储在代码段
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);//malloc需要申请内存,存储在堆上
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);

	free(ptr1);//存储在栈上
	free(ptr2);
}
  • num1非静态局部变量,存储在栈上,在栈上开辟十个int类型的空间,存储数据。
  • char char2[] = "abcd";其中"abcd"作为只读常量存储在代码段,char1为非静态局部变量,存储在栈上,在栈上开辟空间,并拷贝代码段上的"abcd",*char2指向栈上的''abcd''的首元素(存储在栈上)。
  • const char* pChar3 = "abcd";其中pChar3为非静态局部变量,存储的是"abcd"的地址,*pChar3指向的是代码段上"abcd"的首个元素(存储在代码段)。
  • int* ptr1 = (int*)malloc(sizeof(int) * 4);其中ptr1为非静态局部变量,存储在栈上,malloc在堆上开辟空间,*ptr1指向堆(存储在堆上)

sizeof vs strlen

  • sizeof: 计算变量占用内存大小(包括\0)
  • strlen: 计算字符串长度(不含\0)
char s[] = "abcd"; 
sizeof(s); // 5 (包含\0)
strlen(s); // 4

2. 动态内存管理方式

2.1 C

  • malloc:分配未初始化内存。
  • calloc:分配并初始化为0。
  • realloc:调整已分配内存大小(可能迁移数据)。
  • free:释放内存。

三者的区别

函数

初始化

参数

malloc

size_t size

calloc

是(0)

size_t num, size

realloc

保留原数据

void* ptr, size_t new_size

3.2 C++

通过new/delete操作符进行动态内存管理

内置类型操作

void Test2()
{
	int* ptr4 = new int;//申请一个int类型的的空间,无初始化
	int* ptr5 = new int(10);//申请一个int类型的的空间,初始化为10
	int* ptr6 = new int[10];//申请一个10个int类型的的空间,无初始化
	int* ptr7 = new int[10] {};//申请一个10个int类型的的空间,初始化为0
	int* ptr8 = new int[10] {1, 2, 3};//申请10个int类型的空间,前三个初始化为1,2,3,剩余的初始化为0

	//释放单个开辟的空间
	delete ptr4;
	delete ptr5;
	//释放连续开辟的空间
	delete[] ptr6;
	delete[] ptr7;
	delete[] ptr8;
}
  • int* ptr5 = new int(10); new:操作符,用于申请单个元素空间。int:申请空间存储的数据类型,决定申请空间的大小。10:用于初始化申请空间中存储的元素的初始值。
  • delete ptr5;delete操作符,用于释放单个元素空间。ptr5:释放空间元素的位置
  • int* ptr8 = new int[10] {1, 2, 3};new[]:操作符,用于申请连续的空间。10:申请连续空间中能存储的元素个数。int:申请空间存储的数据类型,决定申请空间的大小。{1,2,3}在申请的空间中,将前三个元素初始化为1,2,3,其余的元素初始化为0.
  • delete [] ptr8:delete [] ,用于释放连续的空间。ptr8:释放连续空间的头部元素元素的地址。

自定义类型操作:在自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会.

void Test3()
{
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);//开辟一个A类型的空间,调用自定义类型的构造函数
	free(p1);
	delete p2;//调用自定义类型的析构


	A* p3 = (A*)malloc(sizeof(A) * 10);
	A* p4 = new A[10];//开辟10个A类型的空间,并调用10次初始化
	free(p3);
	delete[] p4;//为每个对象调用析构
}

3. operator new 与 operator delete

  • new和delete是申请和释放内存的操作符,operator new和operator delete是系统提供的全局函数。
  • new在底层调用operator new全局函数申请空间,实际通过malloc来申请。
  • delete在底层调用operator delete全局函数释放空间,实际通过free释放空间。

3.1 new与operator的实现原理

内置类型:

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

  • new/delete申请和释放的是单个元素的空间
  • new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL

自定义类型:

  • new的原理:1. 调用operator new函数申请空间 2. 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理:1. 在空间上执行析构函数,完成对象中资源的清理工作 2. 调用operator delete函数释放对象的空间
  • new T[N]的原理:1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请 ,同时在申请存储N个元素的首个空间前端会多申请4个字节,用于存储申请空间的个数N 2. 在申请的空间上执行N次构造函数。
  • delete[]的原理 1. 在释放的对象空间上,先调用顶端中存储元素的个数N,再执行N次析构函数,完成N个对象中资源的清理 2. 在operator delete[]中调用operator delete来释放空间。

malloc/free vs new/delete

特性 malloc/free new/delete
类型 函数 操作符
初始化 是(可定制)
失败处理 返回NULL 抛出异常
构造/析构 不调用 调用

4. 定位(Placement-new)

  • 作用:在已分配的内存上显式调用构造函数。
  • 使用场景:内存池(避免频繁分配释放)。

eg1:

char buf[sizeof(A)]; // 预分配内存
A* p = new (buf) A(10); // 在buf上构造对象
p->~A(); // 显式调用析构(不释放buf)

eg2: 

class A
{
private:
	int _a;
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a = 0),地址:" << this << endl;
	}
	A(const A& a)
		:_a(a._a)
	{
		cout << "A(const A& a)" << endl;
	}

	~A()
	{
		cout << "~A(),析构地址:" << this << endl;
	}
	int GetNum()
	{
		return _a;
	}
};
int main()
{
	A* p1 = (A*)malloc(sizeof(A));//这里p1指向的不过是与A对象大小相同的一段空间,构造函数没有执行,还不是一个对象
	new(p1)A;//为已分配空间的p1初始化,如果A的构造函数有参数时,需要传参
	p1->~A();//单个元素析构
	free(p1);//释放空间

	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);
	p2->~A();
	operator delete(p2);

	A* p3 = (A*)operator new[](sizeof(A)*10);
	for(int i = 0; i < 10; i++)//相当于new[10],开辟十个连续的空间
	{
		new(p3 + i)A(i);
		//cout << (p3 + i)->GetNum() << endl;
	}
	for(int i = 0; i < 10; i++)//析构10个连续的空间
	{
		(p3+i)->~A();
	}
	operator delete[](p3);//释放连续空间内存

	return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到