C++第七弹 -- C/C++内存管理

07-17 1043阅读

目录

  • 前言
  • 一. C/C++内存分布
  • 二. C语言中动态内存管理方式
  • 三. C++中动态内存管理
  • 四. operator new与operator delete函数
  • 五. new和delete的实现原理
    • 1.内置类型
    • 2. 自定义类型
    • 六. 定位new表达式(placement-new)
    • 七. 常见面试题
    • 总结

      前言

      在C/C++编程中,内存管理是至关重要的一个环节。程序员需要合理地分配和释放内存,以确保程序能够正常运行,避免内存泄漏和崩溃。本文将深入探讨C/C++内存管理机制,从内存分布、动态内存管理方式、new和delete的实现原理到定位new表达式,以及malloc/free和new/delete的区别,全面解析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在哪里?____ C  staticGlobalVar在哪里?____C
         staticVar在哪里?____C   localVar在哪里?____A
         num1 在哪里?____A
         
         char2在哪里?____A   *char2在哪里?___A
         pChar3在哪里?____A      *pChar3在哪里?____D
         ptr1在哪里?____ A       *ptr1在哪里?____B
      2. 填空题:
         sizeof(num1) = ____;40
         sizeof(char2) = ____;5      strlen(char2) = ____;4
         sizeof(pChar3) = ____;4     strlen(pChar3) = ____;4
         sizeof(ptr1) = ____;4
      3. sizeof 和 strlen 区别?
      
      

      char2会在栈中存储, 而*char2内字符串在常量区, 但是因为是使用数组,所以会将abcd\0从常量区拷贝一份到栈区的数组

      C++第七弹 -- C/C++内存管理

      sizeof 和 strlen 区别?

      1. 定义和类型

        sizeof:是一种运算符,用于计算对象或类型所占的空间大小(以字节为单位)。它在编译时就已经确定了值,并将结果在编译时嵌入到程序中。在头文件中,sizeof 通常被 typedef 为 unsigned int。

        strlen:是一种函数,用于计算字符串的长度,即字符串中字符的数量(不包括结束符 \0)。它定义在 头文件中,其原型为 size_t strlen(const char* str);。strlen 在运行时才能计算字符串的长度。

      2. 用法

        sizeof:

        可以用于基本数据类型(如 int、float 等)。

        可以用于数组、指针、结构体、联合等复合数据类型。

        可以直接作用于变量名或类型名,而不需要变量名或类型名被括号括起来(但使用括号可以提高代码的可读性)。

        对于指针,sizeof 返回的是指针本身的大小,而不是指针所指向的内存区域的大小。

        strlen:

        只能用于字符型指针(char*),且该指针必须指向以 \0 结尾的字符串。

        strlen 的参数必须是字符串的起始地址。

      3. 功能和结果

        sizeof:

        返回的是对象或类型所占的空间大小,单位是字节。

        对于数组,sizeof 返回的是整个数组所占的空间大小,包括所有元素和末尾的 \0(如果数组是字符数组的话)。

        对于指针,sizeof 返回的是指针本身的大小,与指针所指向的数据类型无关。

        strlen:

        返回的是字符串中字符的数量,不包括结束符 \0。

        如果传入的指针不是以 \0 结尾的,strlen 会继续读取内存直到遇到 \0 为止,这可能会导致越界访问。

      C++第七弹 -- C/C++内存管理

      【说明】

      1. 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
      2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口

        创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)

      3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
      4. 数据段–存储全局数据和静态数据。
      5. 代码段–可执行的代码/只读常量。

      二. 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 );
      //不需要,如果realloc成功, 会在p2的基础上进行扩容, 返回的是p2
      //如果扩容失败,realloc会自动释放p2的空间,然后再重新找一块更大的空间,然后返回这块空间的地址.
      }
      

      malloc/calloc/realloc的区别?

      C++第七弹 -- C/C++内存管理

      C++第七弹 -- C/C++内存管理

      3. 使用场景

      malloc:适用于需要动态分配内存,但不需要初始化内存内容的场景。

      calloc:适用于需要动态分配内存,并且希望内存内容初始化为0的场景。calloc的初始化特性使得它在分配内存后无需再进行额外的初始化操作。

      realloc:适用于已分配的内存块大小不足以满足当前需求,或者分配了过大的内存块需要缩减的场景。realloc能够调整内存块的大小,同时尽可能保留原内存块中的数据。

      4. 注意事项

      使用malloc、calloc和realloc分配的内存都必须通过free函数来释放,以避免内存泄漏。

      realloc在调整内存块大小时,如果成功则返回新的内存块地址(可能与原地址相同),如果失败则返回NULL,但原内存块仍然保持有效。因此,在使用realloc时,通常需要用一个临时指针来接收realloc的返回值,并在检查返回值后再决定是否更新原指针。

      calloc分配的内存块会被初始化为0,这可能会带来一些额外的性能开销。如果不需要初始化内存内容,使用malloc可能更加高效。

      三. C++中动态内存管理

      C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因

      此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

      对于内置类型

      int main()
      {
      	//对于内置类型,除了用法方便,和malloc没什么区别
      	int* p1 = new int;
      	int* p2 = new int[10];
      	//默认不初始化, 但是可以初始化
      	int* p3 = new int(10);//初始化为10
      	int* p4 = new int[10] {1, 2, 3, 4};
      	delete p1;
      	delete[] p2;
      	return 0;
      }
      

      C++第七弹 -- C/C++内存管理

      对于自定义类型

      class A
      {
      public:
      	A(int a = 0)
      		:_a(a)
      	{
      		cout 
      		cout 
      		cout 
      		cout 
      			_a = aa._a;
      		}
      		return *this;
      	}
      	~A()
      	{
      		cout 
      	//对于自定义类型, new能够调用构造初始化,malloc不在适用
      	//A* p1 = new A;
      	//A* p2 = new A(2);//传参构造
      	//delete p1;//会调用析构函数
      	//delete p2;
      	A* p1 = new A[10];//连续申请10个空间
      	//会调用十次默认构造
      	//不想调用默认构造,下面是拷贝构造
      	A aa1(1);
      	A aa2(2);
      	A aa3(3);
      	A* p2 = new A[10]{ aa1,aa2,aa3 };
      	A* p3 = new A[10]{ 1,2,3,4,{6,7} };//也可以直接写,进行隐式类型转化
      	
      	delete p1;
      	delete p2;
      	delete p3;
      }
      
      	//对于自定义类型,delete[]需要保存需要析构的次数
      	// 所以会多开辟四个字节进行存储析构的次数
      	// 然后释放空间的时候会-4释放掉多开辟的空间
      	// 而delete和free不会,所以会报错 
      	//A* p1 = new A;
      	A* p2 = new A[10];  //44 or 40
      	delete[] p2;
      	delete p2;
      	free(p2);
      	//对于内置类型无影响
      	int* p3 = new int[10];  //40
      	//free(p3);
      	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)
      
      	//A* p1 = new A;
      	//不想通过new进行申请,使用operator new需要手动调用构造函数
      	A* p1 = (A*)operator new(sizeof(A));
      	//p1-A(); // 编译器不支持这样显示调用构造
      	//new(p1)A;  // 对已有空间,显示调用构造
      	new(p1)A(10);  // 对已有空间,显示调用构造
      	//后面回学到的内存池调用
      	//A* p1 = pool.alloc(sizeof(A));
      	//new(p1)A(10);  // 对已有空间,显示调用构造
      	
      	// delete p1
      	p1-~A();//析构编译器支持
      	operator delete(p1);
      	
      	// new []
      	A* p2 = (A*)operator new[](sizeof(A)*10);
      	//new(p2)A[10]{1,2,3,4};  // 对已有空间,显示调用构造
      	for (int i = 0; i 
VPS购买请点击我

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

目录[+]