从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题(上)

简介: 从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题

1. C语言动态内存管理

1.1 C和C++内存分布

C和C++内存分布都是一样的。

栈区(stack)

       栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。

执行函数时,函数内部局部变量的存储单元都可以在栈上创建。

函数执行结束后这些存储单元会被自动释放。栈内存分配运算内置于处理器的指令集中,

拥有很高的效率,但是分配的内存容量是有限的。

栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。


堆区(heap)

       堆用于程序运行时动态内存分配,堆是可以上增长的。

一般由程序员自主分配释放,若程序员不主动不释放,程序结束时可能由操作系统回收。

其分配方式类似于链表。

数据段(data segment)

静态存储区,数据段存放全局变量和静态数据,程序结束后由系统释放。


代码段(code segment)

       可执行的代码 / 只读常量。代码段存放类成员函数和全局函数的二进制代码。

一个程序起来之后,会把它的空间进行划分,而划分是为了更好地管理。

函数调用,函数里可能会有很多变量,函数调用建立栈帧,栈帧里存形参、局部变量等等。

内存映射段(memory mapping)(了解)

       内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。

用户可使用系统接口创建共享共享内存,做进程间通信。


结合以前的知识观察下面一段代码,并回答问题:

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) = ____;


答案解析:


1.2 C语言中动态内存管理的方式

面试题1:malloc / calloc / realloc 的区别?

这三个函数我们以前学C语言已经学过了:

C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_GR C的博客-CSDN博客

相同点

  • 1.都是从堆上申请空间
  • 2.都需要对返回值判空
  • 3.都需要用户free释放
  • 4.返回值类型相同,都是void*
  • 5.都需要类型转化
  • 6.底层实现上是一样的,都需要开辟多余的空间,用来维护申请的空间

不同点:

  • 1.函数名字不同和参数类型不同。
  • 2.calloc会对申请空间初始化,并且初始化为0,而其他两个不会。
  • 3.malloc申请的空间必须使用memset初始化

4.realloc是对已经存在的空间进行调整,当第一个参数传入NULL的时候和malloc一样,调整分为两种情况:

① 调整的空间比原有空间大:

1.大了一点:要调整的多出来的空间小于原有空间后面空闲的空间,

做法:

1.1 直接延伸申请空间
1.2 返回原空间首地址**

2.大了很多:要调整的多出来的空间大于于原有空间后面空闲的空间,

做法:
2.1 重新开辟新空间
2.2 将旧空间的内容拷贝到新空间中
2.3 释放旧空间
2.4 返回新空间的首地址

  • ② 调整的空间比原有空间小:
    做法:
    1.将原空间缩小
    2 .返回旧空间首地址


面试题2:malloc的实现原理?(以下现在做简单了解)

从操作系统角度看,进程分配内存有两种方式,分别由两个系统调用完成:brk 和 mmap (不考虑共享内存)


brk 是将数据段(.data)的最高地址指针 _edata 往高地址推

mmap 是在进程的虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空闲的虚拟内存

这两种方式分配的都是虚拟内存,并没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,会发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系、

情况一:malloc 小于 128K 的内存,使用 brk() 函数分配

情况二:malloc 大于 128K 的内存,使用 mmap 分配(munmap 释放)

一个拓展链接:【CTF】GLibc堆利用入门-机制介绍_哔哩哔哩_bilibili


2. C++动态内存管理方式

#include<iostream>
using namespace std;
 
int main()
{
  int* p1 = (int*)malloc(sizeof(int));
  int* p2 = (int*)malloc(sizeof(int) * 5);
 
  return 0;
}

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

(比如写链表的时候要多次申请空间),因此C++又提出了自己的内存管理方式:

通过newdelete操作符进行动态内存管理

2.1 new/delete操作内置类型

new/delete 的用法:

#include<iostream>
using namespace std;
 
int main()
{
  // 动态申请一个int类型的空间
  int* p1 = new int;
 
  // 动态申请一个int类型的空间并初始化为10
  int* p2 = new int(10);
 
  // 动态申请10个int类型的空间
  int* p3 = new int[10];
 
  //释放申请的空间,并置空
  delete p1;
  p1 = nullptr;
 
  delete p2;
  p2 = nullptr;
 
  delete[] p3;
  p3 = nullptr;
 
  return 0;
}

申请和释放单个元素的空间,使用 new delete 操作符,

申请和释放连续的空间, 使用 new[ ] delete[ ] ,注意:匹配起来使用。

是不是非常的方便,而且 new 不需要强制类型转换。

2.2 初始化new数组的问题

C++98 不支持初始化 new 数组:

int* p = new int[5];

但现在已经202X年了,C++11 允许大括号初始化,

我们就可以用 { } 列表初始化:(后面讲C++11还会讲这个语法)

int* p4 = new int[5] {1, 2};         // 1 2 0 0 0
int* p5 = new int[5] {1, 2, 3, 4, 5};  // 1 2 3 4 5

2.3 new 和 delete 操作自定义类型

       malloc / free 和 new / delete 对于内置类型没有本质区别,

那么它存在的意义是什么呢?仅仅是因为用法更简洁吗?

巨佬为了这么点事设计出来?可以但没必要,看看作用:

#include<iostream>
using namespace std;
 
class A
{
public:
  A(int a = 0)
    : _a(a)
  {
    cout << "A():" << this << endl;
  }
  ~A()
  {
    cout << "~A():" << this << endl;
  }
private:
  int _a;
};
 
int main()
{
  // 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;
}

new 不仅会开内存,还会调用对应的构造函数初始化,

相对的,free 只是把 p1 p2 指向的空间释放掉。

而 delete 不仅会释 p1 p2 指向的空间,delete 还会调用对应的析构函数。

new / delete 不仅仅是写法上变得简单了, 可以说就是为了自定义类型弄出来的。

(还有以后学的抛异常,下面讲operator new函数也简单的讲了一下)


new 对应的是 delete,可以可以 new 出来的用 free 释放?

不建议大家混着用, new 出来的用 free,有的编译器就会报错,

new[ ] 出来的 你去 delate 而不是 delete[ ]  也可能会报错。


总结:

在申请自定义类型的空间时,new 会调用构造函数,

delete 会调用析构函数,而 malloc 与 free 不会。

new:在堆上申请空间 + 调用构造函数输出。

delete:先调用指针类型的析构函数 + 释放空间给堆上。

匹配使用:malloc/free ,delete/delete ,new[ ] / delete[ ]

从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题(中):https://developer.aliyun.com/article/1513662

目录
相关文章
|
20天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
31 3
|
30天前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
37 0
【C++打怪之路Lv6】-- 内存管理
|
1月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
1月前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
1月前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
18 0
|
1月前
|
C++
C/C++内存管理(下)
C/C++内存管理(下)
46 0
|
1月前
|
存储 Linux C语言
C/C++内存管理(上)
C/C++内存管理(上)
37 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
366 0
|
21天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
43 1
|
25天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。