当你改错一行代码的时候:
当你想要重构别人的代码时:
目录
前言
一、C/C++的内存分布
二、C/C++语言中的动态内存管理
三、new与delete的实现原理
总结:
前言
在C++中,内存管理是一个至关重要的主题。正确地管理内存可以避免内存泄漏和内存访问错误等问题,提高程序的性能和可靠性。本文将深入探讨C++中的内存管理机制,包括从C语言内存管理到C++内存管理转变,二者的关联等内容。
一、C/C++的内存分布
C/C++程序内存主要使用了以下几个区域,让我们先来认识一下:
1、内核区域:
“内核区域”通常指的是操作系统的核心部分,它是操作系统的基本组成部分之一。内核负责管理计算机的硬件资源,并提供给应用程序访问这些资源的接口。但是我们用户的代码读写不了它,所以这里也不主要讲解。
2、栈区:
栈区是计算机内存中的一部分,用于存储函数调用时的局部变量、函数参数、函数返回地址等临时数据。栈是一种后进先出(LIFO)的数据结构,因此栈区也被称为后进先出(LIFO)内存区域。
在典型的程序执行过程中,每当一个函数被调用时,该函数的局部变量和参数被分配到栈区,并且该函数的返回地址被推入栈中。当函数执行完成后,栈中的这些数据被销毁,栈的指针回退到上一个函数的栈帧。这种方式使得程序能够有效地管理函数调用和返回。
栈区的大小通常是固定的,由操作系统在程序启动时分配。如果栈区的空间被耗尽,就会发生栈溢出(stack overflow)错误,这通常是由于递归函数调用层数过多或者局部变量占用过多栈空间所导致的。
3、内存映射段:
存映射段(Memory-mapped segment)是指操作系统中的一种机制,它允许将文件或其他设备映射到进程的地址空间,使得这些文件或设备能够像内存一样被访问和操作。(我们这里也不讲解,只是单纯提一下)
4、堆区:
堆区是计算机内存中的一部分,用于动态分配内存空间。在堆区中,程序员可以通过调用诸如 malloc()、calloc() 或 new 等函数来请求内存空间,用于存储程序中动态创建的数据结构,比如链表、树、对象等。(也就是我们今天所要讲解的动态内存管理所涉及的区域)
5、数据段:
数据段(Data Segment)是指存储程序中已声明的全局变量和静态变量的内存区域。数据段通常位于进程的虚拟地址空间中的一个固定位置,并且在程序加载时被分配。(全局变量与static修饰的变量)
(静态变量主要有三种方式:
1、在函数内部使用 static 关键字声明
2、在全局作用域内使用 static 关键字声明
3、在函数内部不使用 static 关键字,但函数外部使用 static 修饰该函数)
6、代码段:
代码段中通常存储着可执行的程序与只读常量如我们规定的“a,n,f,j”等字符
二、C/C++语言中的动态内存管理
1、C语言:
在C语言中,我们通常使用malloc/calloc/realloc/free四个关键字来管理我们的动态内存分配。
malloc()函数:
void *malloc(size_t size);
- 用于动态分配指定大小的内存块。
- 返回一个指向分配的内存块的指针,或者在分配失败时返回 NULL。
calloc()函数 :
void *calloc(size_t num, size_t size);
- 用于动态分配指定数量和大小的内存块,并将其初始化为零。
- 返回一个指向分配的内存块的指针,或者在分配失败时返回 NULL。
realloc()函数;
void *realloc(void *ptr, size_t size);
- 用于更改之前分配的内存块的大小。
- 返回一个指向重新分配的内存块的指针,或者在分配失败时返回 NULL。
free()函数:
void free(void *ptr);
- 用于释放之前分配的内存块。
在C语言中,malloc()等函数会
返回 void*
类型的指针,需要强制类型转换为目标类型,这使得使用十分不方便。而C++内存管理只需要用new与delete两个运算符就可以做得更加高级,更加安全便捷。
2、C++:
C++中用 new
和 delete
运算符来动态地分配和释放内存。
new运算符:
new
运算符用于动态分配内存空间,并返回所分配内存的地址。new
运算符的基本语法为:new 数据类型
或new 数据类型[数组大小]
。- 动态分配的内存必须由程序员显式地释放,否则会造成内存泄漏。
delete运算符:
delete
运算符用于释放动态分配的内存空间。delete
运算符的基本语法为:delete 指针变量
或delete[] 指针变量
(释放数组)。- 动态分配的内存在不再使用时必须由程序员显式地释放,否则会造成内存泄漏。
(其实在C++11中还新增了智能指针的概念,但现在我们先不提及)
int main()
{
int* arr = new int[10];// 分配一个包含10个整数的内存块
int* ptr = new int; // 动态分配一个整数的内存空间
int* ptr2 = new int(10); // 动态分配一个整数的内存空间并且初始化为10
delete ptr;
delete ptr2;//释放内存
delete[] arr;//加上[]表示多个数据
return 0;
}
//分配自定义类型
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A():" << endl;
}
~A()
{
cout << "~A():" << endl;
}
private:
int _a;
};
int main()
{
A* ptr1 = new A;
A* ptr2 = (A*)malloc(sizeof(A));
delete ptr1;
free(ptr2);
return 0;
}
实际上,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。(以后讲解异常概念)operator delete 最终是通过free来释放空间的。
三、new与delete的实现原理
1、对于内置类型来说:
new与malloc,delete与free基本类似,不同的地方是:
new与delete申请与释放的是单个元素的空间,new[]与delete[]申请与释放的是连续的空间,而且new在申请失败时会抛出异常,malloc则会直接返回NULL。
2、对于自定义类型来说:
new的原理:
总结:
希望通过本篇文章,能够对你区分C++与C语言内存管理有所帮助。