【C++】C&C++内存管理

03-24 1018阅读

【C++】C&C++内存管理

目录

  • 一、C/C++内存分布
  • 二 、C语言中动态内存管理方式:malloc/calloc/realloc/free
  • 三、 C++内存管理方式
    • 3.1 new/delete操作内置类型
    • 3.2 new和delete操作自定义类型
    • 3.3 长度域
    • 四、operator new与operator delete函数
    • 五、new和delete的实现原理
      • 5.1 内置类型
      • 5.2 自定义类型
      • 六、定位new表达式(placement-new)
      • 七、常见面试题
        • 7.1 malloc/free和new/delete的区别
        • 7.2 内存泄漏
          • 7.2.1 什么是内存泄漏,内存泄漏的危害
          • 7.2.2 内存泄漏分类(了解)
          • 7.2.3 如何避免内存泄漏
          • 结尾

            一、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在哪里?____
            ptr1在哪里?____ *ptr1在哪里?____
            2. 填空题:
            sizeof(num1) = ____;
            sizeof(char2) = ____; strlen(char2) = ____;
            sizeof(pChar3) = ____; strlen(pChar3) = ____;
            sizeof(ptr1) = ____;
            3. sizeof 和 strlen 区别?
            

            解答:

            1、选择题:

            globalVar 是全局变量,在静态区,选C。

            staticGlobalVar 是静态变量,在静态区,选C。

            staticVar 是静态变量,在静态区,选C。

            localVar 是局部变量,在栈上,选A。

            num1 是数组,在栈上,选A。

            char2 是数组,在栈上,选A。

            *char2 :由于char2在栈上,那么‘*char2是数组第一个元素,也在栈上,选A。

            pChar3 是局部变量,在栈上,选A。

            *pChar3 : 由于pChar3是指针,且指向的对象是常量区的常量字符串,*pChar3 是字符串中的第一个元素,那么*pChar3在常量区,选D。

            ptr1是局部变量,在栈上,选A。

            *ptr1 : 由于ptr1是指针,指向的对象是malloc出来的,那么ptr1指向的对象就在堆上,*ptr1 就是对象中的第一个元素,那么*ptr1就在堆上,选B。


            2、填空题

            sizeof(num1) 计算的是num1数组的大小,等于每个元素的大小*有多少个元素,带入数据:4 * 10 = 40 字节。

            sizeof(char2) 计算的是char2数组的大小,等于每个元素的大小*有多少个元素,char2被常量字符串"abcd"初始化,char2数组中除了有"abcd",数组中最后一个元素还有一个隐藏的元素'\0'带入数据:1 * 5 = 5 字节。

            strlen(char2) 计算的是字符串char2的长度,扫码字符串,直到'\0'为止,'\0'之前有四个元素,那么可以得到结果为 4 。

            sizeof(pChar3) 计算的是指针的大小,32位和64位的指针大小分别是 4 和 8,那么这里指针的大小就是 4 或 8 。

            strlen(pChar3) 计算的是pChar3指向字符串的长度,与上面strlen(char2)的解释相同,那么可以得到结果也是 4 。

            sizeof(ptr1) 计算的是指针的大小,那么结果就是 4 或 8。


            3、sizeof 和 strlen 区别?

            sizeof 是一个运算符,而不是函数,用于获取数据类型或变量的字节大小。sizeof 在编译时计算,不会执行运行时操作。

            strlen 是一个函数,用于计算以空字符 '\0' 结尾的字符串的长度。

            strlen 在运行时遍历字符串,直到找到字符串的空字符'\0'。


            二 、C语言中动态内存管理方式:malloc/calloc/realloc/free

            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);
            }
            

            1.malloc/calloc/realloc的区别是什么?

            malloc 用于分配指定大小的内存块,不进行初始化。

            calloc 用于分配指定数量和大小的内存块,并初始化为零。

            realloc 用于可以分配空间也可以更改先前分配的内存块的大小。

            2.这里需要free(p2)吗?

            不需要,如果realloc是原地扩容那么p2就是p3

            如果realloc是异地扩容,那么p2指向的内容赋值给p3后就会被销毁,再次free会导致崩溃。


            三、 C++内存管理方式

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


            3.1 new/delete操作内置类型

            void Test()
            {
            	// 动态申请一个int类型的空间
            	int* ptr4 = new int;
            	// 动态申请一个int类型的空间并初始化为10
            	int* ptr5 = new int(10);
            	// 动态申请3个int类型的空间
            	int* ptr6 = new int[3];
            	delete ptr4;
            	delete ptr5;
            	delete[] ptr6;
            }
            

            【C++】C&C++内存管理

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


            3.2 new和delete操作自定义类型

            class A
            {
            public:
            	A(int a = 0)
            		: _a(a)
            	{
            		cout 
            		cout 
            	// new/delete 和 malloc/free最大区别是 
            	// new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
            	A* p1 = (A*)malloc(sizeof(A));
            	A* p2 = new A(1);
            	free(p1);
            	delete p2;
            	// 内置类型是几乎是一样的
            	int* p3 = (int*)malloc(sizeof(int)); // C
            	int* p4 = new int;
            	free(p3);
            	delete p4;
            	A* p5 = (A*)malloc(sizeof(A) * 10);
            	A* p6 = new A[10];
            	free(p5);
            	delete[] p6;
            	return 0;
            }
            
            public:
            	A(int a = 0)
            		: _a(a)
            	{
            		cout 
            		cout 
            	A* p1 = new A[10];
            	// 若不将析构函数屏蔽那么下面三个析构会出现的情况为
            	// delete p1;     // 不匹配使用,程序运行崩溃
            	// free(p1);	  // 不匹配使用,程序运行崩溃
            	// delete (p1 - 1); //不匹配使用,程序运行正常,但调用析构函数次数不够
            	// free(p1-1);      //不匹配使用,程序运行正常,但未调用析构函数
            	delete[] p1;   // 匹配使用,程序运行正常
            	A* p2 = new A[10];
            	// 将析构函数屏蔽后,编译器生成默认析构函数
            	// 而默认析构函数没做什么事情,编译器优化
            	// 不在指针指向的位置前开辟空间记录申请了几个对象
            	// 那么析构时指针指向的位置就是开辟空间的头,可以直接析构
            	
            	// 若将析构函数屏蔽那么下面三个析构会出现的情况为
            	// delete p2;	      // 不匹配使用,程序运行,由于编译器的优化
            	// free(p2);      // 不匹配使用,程序运行,由于编译器的优化
            	// delete[] p2;   // 匹配使用,程序运行正常
            	return 0;
            }
            
            	// try to allocate size bytes
            	void* p;
            	while ((p = malloc(size)) == 0)
            		if (_callnewh(size) == 0)
            		{
            			// report no memory
            			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
            			static const std::bad_alloc nomem;
            			_RAISE(nomem);
            		}
            	return (p);
            }
            // operator delete: 该函数最终是通过free来释放空间的
            void operator delete(void* pUserData)
            {
            	_CrtMemBlockHeader* pHead;
            	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
            	if (pUserData == NULL)
            		return;
            	_mlock(_HEAP_LOCK); /* block other threads */
            	__TRY
            		/* get a pointer to memory block header */
            		pHead = pHdr(pUserData);
            	/* verify block type */
            	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead-nBlockUse));
            	_free_dbg(pUserData, pHead-nBlockUse);
            	__FINALLY
            		_munlock(_HEAP_LOCK); /* release other threads */
            	__END_TRY_FINALLY
            		return;
            }
            // free的实现
            #define free(p) _free_dbg(p, _NORMAL_BLOCK)
            
            public:
            	A(int a = 0)
            		: _a(a)
            	{
            		cout 
            		cout 
            	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
            	A* p1 = (A*)malloc(sizeof(A));
            	new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
            	p1-~A();
            	free(p1);
            	A* p2 = (A*)operator new(sizeof(A));
            	new(p2)A(10);
            	p2-~A();
            	operator delete(p2);
            	return 0;
            }
            
            	// 1.内存申请了忘记释放
            	int* p1 = (int*)malloc(sizeof(int));
            	int* p2 = new int;
            	// 2.异常安全问题
            	int* p3 = new int[10];
            	Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
            	delete[] p3;
            }
            
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]