【C++初阶:内存管理】C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数 上

简介: 【C++初阶:内存管理】C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数

文章目录

【写在前面】

在 C 语言中我们也学过内存管理,可转至动态内存管理那些事:malloc、calloc、realloc、free

C/C++ 的内存管理跟 JAVA 这些语言是不同的 —— JAVA 的程序不是直接跑在操作系统上的,JAVA 是在 JVM 虚拟机上运行的;C/C++ 的程序是直接跑在 OS 上的,这也是为什么我们学习 C/C++ 要学习内存管理的原因,所以 C/C++ 的学习者需要对系统了解的更深,而对于系统的知识更多的会在 Linux 系统编程的阶段去学习。

这里我们会学习 new/delete 的使用,以及 new/delete 的底层原理。

一、C/C++内存分布

💦 填空题 && 选择题

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
  static int staticVar = 1;
  int localVar = 1;
  const int localVal1 = 1;
  int num1[10] = {1, 2, 3, 4};
  char char2[] = "abcd";
  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);
}

🔑 选项:A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)

  • globalVar 存储于数据段
  • staticGlobalVar 存储于数据段
  • staticVar 存储于数据段
  • localVar 存储于栈
  • localVar1 存储于栈
  • num1 存储于栈
  • char2 存储于栈
  • *char2 存储于栈
  • pChar3 存储于栈
  • *pChar3 存储于代码段
  • ptr1 存储于栈
  • *ptr1 存储于堆

🔑 填空:

  • sizeof(num1) = 40
  • sizeof(char2) = 5
  • sizeof(pChar3) = 4/8
  • sizeof(ptr1) = 4/8
  • strlen(char2) = 4
  • strlen(pChar3) = 4

📝说明

注意这里比较容易出错的是:

💦 C/C++内存分布示意图

📝说明

栈 ❓

栈又叫堆栈,注意区分数据结构中的栈

函数调用建立栈帧,函数中的参数、局部变量都存在栈帧中

栈是向下增长的,比如 main 函数调用 f 函数:

堆 ❓

注意区分数据结构中的堆

malloc、calloc、realloc 都会在堆上开辟空间

堆是向上增长的,理论上后 malloc 的内存地址比先 malloc 的要大,但是也不一定,因为有可能下一次申请的是之前别人释放回来的:

内存映射段 ❓

内存映射段是高效的 I/O 映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。

虚拟内存 | 物理内存 ❓

虚拟内存跟物理内存要按页映射,就是对应起来。程序访问虚拟内存,实际要转到对应的物理内存。

内存区域特点 ❗

  1. 这几个区域堆是很大的 —— 你可以认为如果在 32 位下虚拟内存总共占 4G ,内核占 1G,剩下的 3G 空间大部分都是堆的
  2. 从图看,栈和堆差不多大,实际上栈很小 —— Linux下一般只有 8M,所以递归深度太深,很容易导致栈溢出 (Stack overflow)
  3. 数据段和代码段也不是很大 —— 因为没有多少数据 (全局数据 + 静态数据)

🍳拓展

对于内存划分、虚拟内存和物理内存映射可以去看:

《深入理解计算机系统》-> 虚拟存储器

《程序员的自我修养》-> 编译链接、动态库等

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

💦 malloc/calloc/realloc和free

p2 calloc一块空间后,p3又realloc p2,要对p2 free吗 ❓

void Test ()
{
  int* p1 = (int*) malloc(sizeof(int));
  free(p1);
  int* p2 = (int*)calloc(4, sizeof (int));
  int* p3 = (int*)realloc(p2, sizeof(int)*10);
  free(p3);
}

📝说明

这里涉及了 realloc 的开辟原理

所以对于上面程序的写法并不好:

【面试题】malloc/calloc/realloc的区别

malloc -> 开空间

calloc 等价于 malloc + memset(0) -> 开空间 + 初始化

realloc 单独使用时能实现 malloc 的效果 (不会初始化) -> 开空间 | 对 malloc/calloc 的空间扩容

三、C++内存管理方式

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

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

💦 new/delete操作内置类型

int main()
{
  //库函数
  int* p1 = (int*)malloc(sizeof(int));
  free(p1);
  //操作符/关键字
  int* p2 = new int;
  delete p2;
  return 0;
}

📝说明

malloc/free 和 new/delete 有什么区别 ❓

  • 如果动态申请的对象是内置类型,那么 malloc 和 free 没有区别
  • 如果动态申请的对象是自定义类型,那么 malloc 和 free 有区别

💦 new和delete操作自定义类型

class A
{
public:
  A(int a = 0/*int b = 0*/)
    :_a(a)
  {
    cout << "A()" << endl;  
  }
  ~A()
  {
    cout << "~A()" << endl; 
  }
private:
  int _a;
};
int main()
{
  A* p3 = (A*)malloc(sizeof(A));
  free(p3);
  A* p4 = new A;
  //A* p4 = new A(10);
  //A* p4 = new A(10, 20);
  delete p4;
  //数组
  int* p5 = (int*)malloc(sizeof(int) * 10);
  free(p5);
  int* p6 = new int[10];
  delete[]p6;
  A* p7 = new A[10];//调用10次构造
  delete[]p7;//调用10次析构
  return 0;
}

📝说明

  • 对于内置类型 malloc/free 仅仅会开空间/释放空间
  • 对于自定义类型 new/delete 不仅仅会开空间/释放空间,还会调用构造函数和析构函数 —— 调用构造函数时还可以传参,且可以传多个参数

注意我们在 new A 类时不需要默认构造函数;但是在 new A[10] 时则需要默认构造函数

在 C++ 中建议尽量使用 new/delete,因为 malloc/free 能做到的,new/delete 也能做到;new/delete 能做到的,malloc/free 不一定能做到。

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

这种特性有什么用 ❓

struct ListNode
{
  int _val;
  ListNode* _next;
  ListNode(int val)
    : _val(val)
    , _next(nullptr)
  {}
};
int main()
{
  //C
  ListNode* n1 = (ListNode*)malloc(sizeof(ListNode));
  n1->_val = 1;
  n1->_next = nullptr;
  //C++
  ListNode* n2 = new ListNode(1);
  return 0;
}

牛角尖问题 ❓

int* p1 = (int*)malloc(sizeof(int) * 10);
free(p1);
delete p1;
int* p2 = new int;
delete p2;
free(p2);
int* p2 = new int[10];
delete[]p2;
delete[10]p2;
delete p2;//err
free p2;/err

📝说明

注意,一定要匹配使用:malloc ↔ free 、new ↔ delete、new 类型[] ↔ delete[]类型,否则可能会崩溃。


相关文章
|
2月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
5月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
218 26
|
5月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
333 15
|
10月前
|
存储 程序员 编译器
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
6月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
130 1
|
9月前
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
10月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
464 0
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
187 0
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
280 0

热门文章

最新文章