【C++系列】-----------内存管理
c++内存管理(涉及:数据在内存中的分布、new和delete使用、动态内存管理等)
文章目录
- c++内存管理(涉及:数据在内存中的分布、new和delete使用、动态内存管理等)
- 前言
- 一、C/C++内存分布
- 二、C++中动态内存管理
- 2.1、 new/delete操作内置类型
- 2.2、 new和delete操作自定义类型
- 总结
前言
众所周知,c++没有提供(垃圾)回收机制,所以写C/C++程序常常会面临内存泄漏等问题,但是c++提供了对内存精细控制的方式,允许程序员以动态和手动的方式分配和释放内存。这种能力既带来强大的灵活性,也伴随着一定的挑战。下面我们一起看一下如何使用这些方法。
一、C/C++内存分布
下面我们通过一些经典例题来了解一些
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";//比较特殊,仔细阅读一下
const 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);
free(ptr1);
free(ptr3)
}
1、选择:A、栈 B、堆 C、数据段(静态区) D、代码段(常量区)
globalVar在哪里?____ staticGlobalVar在哪里?____
staticVar在哪里?____ localVar在哪里?____
num1 在哪里?____
char2在哪里?____ *char2在哪里?___
pChar3在哪里?____ *pChar3在哪里?___
通过上面代码我们可以看到:
glovalVar是在Test函数体外创建的变量,即为全局变量,存放在数据段(静态区)中。
变量staticGlobalVar,也处在函数体之外,同时被关键字static修饰,即为全局静态变量,存放在数据段(静态区)中。
staticVar是在Test函数内部创建的静态变量,即为局部静态变量,存放在数据段(静态区)中。
localVar是在Test函数内部创建的变量,即为局部变量,存放在栈区。
num1是在Tese函数内部创建的数组的数组名,即为局部创建的多个普通变量,存放在栈区。
char2是在Test函数内部创建的数组的数组名,同num1。
、* char2是比较特殊的,因为char char2[] = “abcd”;这句代码的实质是用右边的字符串初始化数组(存储在栈中),在代码段(常量区)中有“abcd\0”字符串,拷贝一份存储在char2数组中(abcd只是一份拷贝对象,所占空间还是栈上开辟的)因此 * char2存放在栈中。
pChar3是在Test函数内部创建的const修饰的常指针变量(这里拓展个知识:const在修饰指针时,在*之前修饰,是指 指针指向的),实质还是一个局部创建的变量,只是该变量的值不能修改,因此pChar3存放在栈区。
*pChar3是对数组的的首元素进行解引用,*pChar3是常量字符串的第一个字符,字符常量存放在代码段(常量区)。
*ptr1是对数组的的首元素进行解引用,ptr1是通过动态开辟的空间,动态开在堆区申请空间,因此ptr1存放在堆区。
说明
- 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(了解一下)
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段–存储全局数据和静态数据。
- 代码段–可执行的代码/只读常量
二、C++中动态内存管理
c语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
2.1、 new/delete操作内置类型
void Test()
{
// 用法上,比malloc更简洁,不需要计算类型大小,并且可以对空间内容初始化(这点对内置类型还不太明显)
int* p = (int*)malloc(sizeof(int));
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[10];
// 动态申请10个int类型的空间,并将他们初始化
int* ptr7 = new int[10]{1,2,3,4,5,6,7,8,9,10};
delete ptr4;
delete ptr5;
delete[] ptr6;
delete[] ptr7;
free(p);
}
2.2、 new和delete操作自定义类型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = (A*)malloc(sizeof(A));
if (p1 == NULL)
{
perror("malloc fail");
exit(-1);
}
free(p1);
A* p2 = new A(1);
delete p2;
return 0;
}
函数malloc()、free()与关键字new、delete最大的区别是,new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数,而malloc函数仅会开辟与自定义类型(A)等大字节的空间,并不会完成初始化工作。
总结
这次的博客就到这了,本来计划将new/delete的使用和底层讲一下的,但是篇幅过长,阅读起来会烦躁。