【C/C++】图文题目吃透内存管理

2022-11-15 15:59:59 浏览数 (2)

学习目标:了解C/C 内存的分段情况,C 内容管理方式、operator new与operator delete函数 、new和delete的实现原理、定位new的表达式、最后介绍相关面试题的解析

文章目录
  • 一、C/C 内存分段
  • 二、C语言中动态内存管理方式
  • 三、C 内存管理方式
    • 1.new/delete操作内置类型
    • 2 new和delete操作自定义类型
  • 四、operator new与operator delete函数
  • 五、new和delete的实现原理
    • 1.内置类型
    • 2.自定义类型
  • 六、定位new表达式(placement-new)
  • 七、常见面试题
    • 1 malloc/free和new/delete的区别
    • 2 内存泄漏
      • 2.1什么是内存泄漏
      • 2.2内存泄漏分类(了解)
      • 2.3 如何检测内存泄漏(了解)
      • 2.4如何避免内存泄漏

一、C/C 内存分段

C/C 程序会对内存进行分段

从C语言的角度我们知道:分为静态区

从操作系统的角度我们分为:

对于不同的区域数据有不同的性质,方便管理。

  1. 栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的>。
  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信
  3. 堆用于程序运行时动态内存分配,堆是可以上增长的
  4. 数据段–存储全局数据和静态数据
  5. 代码段–可执行的代码/只读常量

我们先来看下面的一段代码和相关问题,直接进行灵魂拷问:

代码语言:javascript复制
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);
}

globalVar在哪里?全局变量,在数据段(静态区)

staticGlobalVar在哪里?静态变量,在数据段(静态区)

staticVar在哪里?数据段(静态区)

localVar在哪里?局部变量,在栈

num1 在哪里?栈

—————————————————————————————————

char2在哪里?栈

*char2在哪里?"abcd"常量在代码段中,char2在栈中开辟一个数组,在把常量拷贝到数组中去,*char就是a,a在栈中

pChar3在哪里?pChar3是局部变量,是常变量,还是在栈中

*pChar3在哪里?pChar3是一个指针(也就是"abcd"的地址),故*pChar3在代码段(常量区)中

ptr1在哪里?ptr1是一个局部变量的指针,指向在堆上动态开辟的空间,所以ptr1在是在栈上的

*ptr1在哪里?*ptr1在堆上,在堆

sizeof(num1) = 40;

sizeof(char2) = 5;

strlen(char2) = 4;

sizeof(pChar3) = 4/8;

strlen(pChar3) = 4;

sizeof(ptr1) = 4/8;

至此,结束我们的这一道题。


二、C语言中动态内存管理方式

malloc/calloc/realloc和free

代码语言:javascript复制
void Test ()
{
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);
    // 1.malloc/calloc/realloc的区别是什么?
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);
    // 这里需要free(p2)吗?
    free(p3);
}

对于区别,直接看我之前的博客

对于另一个问题,我们知道realloc扩完容之后,原地扩容则p2和p3是一样,如果是异地扩容realloc会把p2释放掉


三、C 内存管理方式

C语言内存管理方式在C 中可以继续使用,但有些地方就无能为力而且使用起来比较麻烦,因此C 又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

1.new/delete操作内置类型

代码语言:javascript复制
int main()
{
	//内置类型
	//相比于malloc/free,除了用法没有其他区别
    
    //动态申请一个int类型的空间
	int* p1 = new int;
	delete p1;
    //动态申请一个int类型的空间并初始化为0
	int* p2 = new int(0);
	delete p2;
    //动态申请10个int类型的空间
	int* p3 = new int[10];
	delete[] p3;
    //动态申请10个int类型的空间,并初始化
	int* p4 = new int[10]{ 1,2,3,4 };
	delete[] p4;
	return 0;
}

注:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]

同时,malloc失败会返回一个空指针

而new失败会抛出异常:

代码语言:javascript复制
void Test()
{
	while (1)
	{
		//new失败,抛出异常——不需要检查返回值
		char* p1 = new char[1024 * 1024 * 1024];
		cout << (void*)p1 << endl;
	}
}

int main()
{
	try
	{
		Test();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

真正的区别在于操作自定义类型

0 人点赞