【C++第五课-C/C++内存管理】C/C++的内存分布、new/delete、new和delete的实现原理

目录

  • C/C++的内存分布
  • new/delete
    • new
      • 内置类型使用new
      • 自定义类型使用new
      • new失败
    • delete
      • 内置类型使用delete
      • 自定义类型使用delete
    • new和delete的实现原理
    • new[] 和delete[]的补充知识
  • 定位new(了解)
  • 常见面试题

C/C++的内存分布

在这里插入图片描述

频繁的new/delete堆容易产生内存碎片,栈上则没有这个问题(因为是连续分配)
栈是先进后出,先进的在内存地址大的
堆无法静态分配,只能动态分配
栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放
不同的数据有不同的存储需求,内存的个区间划分为了满足这些不同的需求
1、临时用(局部变量、数组等) - > 栈
2、动态使用(数据结构、算法中需要动态开辟一些空间) - > 堆
3、整个程序期间都要使用(全局变量,静态变量) - > 静态区/数据段
4、只读数据(常量、可执行代码) - > 代码段/常量区
可执行代码是指二进制代码(电脑可以看懂的那个)
咱写的那个代码存在磁盘上(存在文件中)
在这里插入图片描述

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);
}
选择题
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
1、globalVar在哪里?____ 2、staticGlobalVar在哪里?____
3、staticVar在哪里?____ 4、localVar在哪里?____
4、num1 在哪里?____
5、char2在哪里?____ 6*char2在哪里?___
7、pChar3在哪里?____ 8*pChar3在哪里?____
9、ptr1在哪里?____ 10*ptr1在哪里?____
填空题:
11sizeof(num1) = ____;
12sizeof(char2) = ____;  13strlen(char2) = ____;
14sizeof(pChar3) = ____; 15strlen(pChar3) = ____;
16sizeof(ptr1) = ____;

1、C globalVar是全局变量 - 在静态区
2、C staticGlobalVar是静态全局变量 - 在静态区
3、C staticVar是静态变量 - 在静态区
4、A num1是数组名指的是首元素地址 - 在栈上
5、A char2是数组名 - 在栈上
6、A *char2指向的是根据常量字符串创建的数组 - 在栈上
7、A pchar3是指针 - 在栈上
8、D *pchar3是值得常量字符串 - 在常量区
9、A ptr1是指针 - 在栈上
10、B *ptr1是指针指向的那块动态开辟的空间 - 在堆上

11、40
12、5
13、4 (strlen不算\0)
14、4/8
15、4
16、4/8

在这里插入图片描述

补充
1、下面三者的相同点与不同点
在这里插入图片描述
相同的:生命周期都是全局
不同点:作用域不同。globalVar是所有文件都可以使用;staticGlobalVar是只能当前文件使用;staticVar是只能在Test函数里面使用
2、const
(1)const char* pChar3 = "abcd" const修饰的是pChar3所指向的内容不能改变
(2)char* const pChar3 = "abcd" 这个const才是修饰pChar3这个指针的内容不能被修改
(3)const int n = 10 n是常变量
3、字面量
10、‘x’、“111111111”、1.1
4、calloc:开辟空间并初始化为0

new/delete

new

内置类型使用new

使用方法:new+类型
new对于内置类型不会初始化, 初始化(一个数据用(),多个数据用{})

//申请一个int空间
int* p1 = new int;
int* p2 = new int[3];//初始化
int* p3 = new int(3);
int* p41 = new int[3]{1, 2, 3};
int* p4 = new int[10]{1, 2, 3};//不用10个都初始化,前三个是123,后七个是0//释放
delete p1;
delete[] p2;
delete p3;
delete[] p41;
delete[] p4;

在这里插入图片描述

自定义类型使用new

malloc不方便解决动态申请的自定义类型对象的初始化问题
new:1、开空间; 2、调用构造函数

new失败

new失败了会抛异常,异常必须被捕获(继承和多态讲)
出错了之后会直接到捕获的地方(往catch的地方跳)

delete

内置类型使用delete

使用方法:new的就用delete;new[] 的就用delete[]

自定义类型使用delete

delete:析构函数 + 释放空间

new和delete的实现原理

new:operater new + 调用构造函数
delete:调用析构函数 + operater delete

operater new:封装了malloc,加了个出错时的报错机制
operater delete:封装了free,加了个出错时的报错机制
operater new和operater delete是函数,new和delete是操作符

new[] 和delete[]的补充知识

class Stack
{
public:Stack(int capacity = 4):_a(nullptr),_top(0),_capacity(capacity){_a = new int[capacity];}~Stack(){delete[] _a;_a = nullptr;_top = 0;_capacity = 0;}
private:int* _a;int _top;int _capacity;
};int main()
{Stack* p3 = new Stack[10];delete[] p3;return 0;
}

1、new[]的底层
因为p3指向的是个数据,空间上是连续的,因此new只调用了一次
在这里插入图片描述

2、new Stack[10]这个开辟的大小本应该120,但实际上是124
(1)new[]偷偷在最前面开了4字节放-- 一共开的对象数目

在这里插入图片描述
在这里插入图片描述

(2)delete[] 就会先将指针向前移动4字节,然后根据其内容知道是调用10次析构函数
(3)正是由于(2)条,delete时就不会将指针向前移动四个字节,因此报错
(4)但有时即使像(3),但也不会报错

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& i){_a = i._a;cout << "A(const A& i)" << endl;}/*~A(){cout << "~A()" << endl;}*/
private:int _a;
};
int main()
{A* p2 = new A[10];delete p2;return 0;
}

在这里插入图片描述

原因:
因为A没有析构函数,所以系统任务A的对象没有什么可析构的,调不调析构函数都可以,因此在new[]时会进行优化,直接不创建最前面的四个字节来显示一个创建了几个A对象
因此在delete的时候本来就不需要向前移动四个字节,所以不会报错

定位new(了解)

在已分配的原始内存空间中调用构造函数初始化一个对象

new(place_address)type或者new(place_address)type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表

int main()
{A* p3 = (A*)operator new(sizeof(A));//定位new去调用构造函数,构造函数不能显示调用new(p3)A(1);//析构函数可以显示调用p3->~A();return 0;
}

定位new去调用构造函数,构造函数不能显示调用
析构函数可以显示调用

在这里插入图片描述
虽然上面的两行相当于new,一般情况下我们会用new(方便)
特殊情况我们也会用那两行
频繁申请小对象是,需要频繁的去堆上开辟空间池化技术,内存池
在这里插入图片描述

内存池
1、内存池申请对象空间
2、定位new显示调用构造函数
3、释放对象:显示调用析构函数,将内存还回内存池

常见面试题

用法+原理
malloc/free和new/delete的区别?
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地
方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,
    如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需
    要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new
    在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成
    空间中资源的清理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/783454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

拦截器未生效的问题

记录一下自己出现的一个问题 配置好拦截器后 protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/**").excludePathPatterns(&q…

【Java 多线程】从源码出发,剖析Threadlocal的数据结构

文章目录 exampleset(T value)createMap(t, value);set(ThreadLocal<?> key, Object value)ThreadLocalMap和Thread的关系 全貌 ThreadLocal是个很重要的多线程类&#xff0c;里面数据结构的设计很有意思&#xff0c;很巧妙。但是我们平时使用它的时候常常容易对它的使用…

WPF+Prism 模块化编程(一)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 WPFPrism 模块化编程&#xff08;一&#xff09; 一、Prism项目创建安装二、将项目升级为Prism项目三、将Prism项目升级为支持模块化编程项目 一、Prism项目创建安装 1、新建…

算法系列--动态规划--特殊的状态表示--分析重复子问题

&#x1f495;"轻舟已过万重山!"&#x1f495; 作者&#xff1a;Lvzi 文章主要内容&#xff1a;算法系列–算法系列–动态规划–特殊的状态表示–分析重复子问题 大家好,今天为大家带来的是算法系列--动态规划--特殊的状态表示--分析重复子问题 一.组合总数IV 链接…

蓝桥集训之游戏

蓝桥集训之游戏 核心思想&#xff1a;博弈论 区间dp 设玩家1的最优解为A 玩家2的最优解为B 1的目标就是使A-B最大 2的目标就是使B-A最大 当玩家1取L左端点时 右边子区间结果就是玩家2的最优解B-A 即当前结果为w[L] – (B-A) 当玩家1取R右端点时 左边子区间结果就是玩家2的最…

Mybatis-特殊SQL的执行

1. 模糊查询 在MyBatis中进行模糊查询时&#xff0c;有以下三种常见的实现方式&#xff1a; 1.1. 错误示范 先来个准备操作&#xff0c;并做一个错误示例 根据姓名&#xff0c;模糊查询用户&#xff0c;(x小x) 更新数据表 SQLMapper.java package com.sakurapaid.mybatis3…

Win10 搭建FTP存储服务器站点【超详细教程】

目录 第一步&#xff1a;打开控制面板>程序 第二步&#xff1a;win10左下角搜索IIS并打开 第三步&#xff1a;右键网站&#xff0c;选择添加FTP站点 第四步&#xff1a;添加FTP站点名称 第五步&#xff1a;添加IP地址和端口 第六步&#xff1a;身份验证与授权信息 第…

浅谈Spring体系的理解

浅谈Spring知识体系 Spring Framework架构图Spring家族技术生态全景图XMind汇总 本文不涉及细节&#xff0c;主要回答两个问题&#xff1a; Spring家族技术生态全景图有哪些Spring Framework架构下每个模块有哪些东西&#xff0c;以及部分模块之间的关联关系 Spring Framework架…

C语言操作符详细讲解

前言 本次博客一定会让刚刚学习C语言小白有所收获 本次操作符讲解不仅分类还会有代码示例 好好看 好好学 花上几分钟就可以避免许多坑 1 操作符的基本使用 1.1操作符的分类 按功能分 算术操作符&#xff1a; 、- 、* 、/ 、% 移位操作符: >> << 位操作符…

华为OD机试 - 芯片资源限制(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

【数据分享】1981-2023年全国各城市逐日、逐月、逐年最高气温(shp格式)

气象数据是我们在各种研究中都会使用到的基础数据&#xff0c;之前我们分享了Excel格式的1981-2023年全国各城市的逐日、逐月、逐年最高气温数据。 好多小伙伴拿到数据后问我们有没有GIS矢量格式的该数据&#xff0c;我们专门对数据进行了处理&#xff0c;转换为了GIS矢量格式…

【git】git使用手册

目录 一 初始化 1.1 账号配置 1.2 ssh生成 1.2.1 配置ssh 1.2.2 测试SSH 1.3 初始化本地仓库并关联远程仓库 二 使用 2.1 上传 2.2 拉取 三 问题 3.1 关联失败 一 初始化 git的安装很简单,下载后大部分进行下一步完成即可----->地址: git工具下载 1.1 账号配置…

金额转换.java

题目&#xff1a; 奖金额转换成大写的七位数 分析&#xff1a;获取每一位数字&#xff0c;将数字转为大写的&#xff0c;数字前面添零&#xff0c;补成七位数&#xff0c;最后依次拼接单位 package text; import java.util.Scanner; public class MoneySwitch {public static v…

Qt/QML编程之路:QPainter与OpenGL的共用(49)

在Qt编程中,有时会有这样一种场景:用OpenGL显示了一个3维立体图,但是想在右下角画一个2D的表格,里面写上几个字。那么这个时候就会出现QPainter与OpenGL共用或者说2D、3D共用。但是问题是调用了QPainter,drawline之后呢,OPenGL的状态被清空了丢失了,3D不显示了。 在Ope…

算法学习——LeetCode力扣动态规划篇5(198. 打家劫舍、213. 打家劫舍 II、337. 打家劫舍 III )

算法学习——LeetCode力扣动态规划篇5 198. 打家劫舍 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统…

Python学习之多线程、多进程

前言: 在了解本章之前&#xff0c;我们先来了解下什么是线程和进程: 在计算机科学中&#xff0c;进程和线程是执行程序的基本单元&#xff0c;它们在操作系统的管理下运作&#xff0c;但它们之间有着本质的区别。理解进程和线程的概念对于进行有效的程序设计和系统管理非常重要…

(C语言)fread与fwrite详解

1. fwrite函数详解 头文件&#xff1a;stdio.h 函数有4个参数&#xff0c;只适用于文件输出流 作用&#xff1b;将从ptr中拿count个大小为size字节的数据以二进制的方式写到文件流中。返回写入成功的数目。 演示 #include <stdio.h> int main() {FILE* pf fopen(&qu…

Ruoyi-Cloud-Plus_使用Docker部署分布式微服务系统_环境准备_001---SpringCloud工作笔记200

1.首先安装docker: 如果以前安装过首先执行: yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 去卸载docker 2.安装dokcer需要的工具包…

如何使用固定公网地址远程访问内网Axure RP生成的网站原型web页面

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

GPT提示词分享 —— 智能域名生成器

提示词&#x1f447; 我希望你能充当一个聪明的域名生成器。我将告诉你我的公司或想法是什么&#xff0c;你将根据我的提示回复我一份域名备选清单。你只需回复域名列表&#xff0c;而不是其他。域名应该是最多 7-8 个字母&#xff0c;应该简短但独特&#xff0c;可以是朗朗上口…