从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

目录
相关文章
|
3月前
|
Java
【Java基础面试三十二】、new String(“abc“) 是去了哪里,仅仅是在堆里面吗?
这篇文章解释了Java中使用`new String("abc")`时,JVM会将字符串直接量"abc"存入常量池,并在堆内存中创建一个新的String对象,该对象会指向常量池中的字符串直接量。
|
20天前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
43 1
|
28天前
|
网络协议 编译器 Linux
【C语言】结构体内存对齐:热门面试话题
【C语言】结构体内存对齐:热门面试话题
|
23天前
|
程序员 编译器 C语言
C中的 malloc 和C++中的 new 有什么区别
在C语言中,`malloc`函数用于在运行时分配内存,返回指向所分配内存的指针,需显式包含头文件 `&lt;stdlib.h&gt;`。而在C++中,`new`不仅分配内存,还对其进行构造初始化,且直接使用类型声明即可,无需额外包含头文件。`new`还支持数组初始化,能更好地融入C++的面向对象特性,而`malloc`仅作为内存分配工具。使用完毕后,`free`和`delete`分别用于释放`malloc`和`new`分配的内存。
47 21
|
20天前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
38 2
|
25天前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
6天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
9 0
|
28天前
|
编译器 C语言 C++
【C语言】精妙运用内存函数:深入底层逻辑的探索
【C语言】精妙运用内存函数:深入底层逻辑的探索
|
28天前
|
Serverless 编译器 C语言
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
|
2月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。