C++语法|C++八股|内存分区、内存对齐、野指针和悬浮指针

文章目录

  • 内存分区
    • 堆(heap)和栈(stack)的区别
    • new和malloc的区别
    • delete和free有什么区别
  • 野指针
    • 导致野指针的原因
    • 如何避免野指针
    • 野指针和悬浮指针
  • 内存对齐
    • 什么是内存对其
    • 为什么要内存对齐
    • 内存对其的规则

内存分区

从高地址到低地址依次是:

  • 栈(stack)区:由编译器自动释放、分配。存放局部变量、形参和返回值
  • 堆(heap)区:由程序员分配内存和释放。调用malloc()\newfree()\delete()
  • 全局(静态)区:这部分可以细分为data区、bss区、常量区
    • .data区里主要存放的是已经初始化的全局变量、静态变量和常量
    • .bss区主要存放的是未初始化的全局变量、静态变量,这些未初始化的数据在程序执行前会自动被系统初始化为0或者NULL
    • .rodata常量区是全局区中划分的一个小区域,也被称为只读区.里面存放的是常量,如const修饰的全局变量、字符串常量等
  • .text代码区:存放函数体的二进制代码,由操作系统进行管理的

堆(heap)和栈(stack)的区别

栈是⼀种有限的内存区域,⽤于存储局部变量、函数调⽤信息等。
堆是⼀种动态分配的内存区域,⽤于存储程序运⾏时动态分配的数据。

变量的生命周期:栈上的变量⽣命周期与其所在函数的执⾏周期相同,⽽堆上的变量⽣命周期由程序员显式控制,可以(使⽤ 或 malloc )和释放(使⽤ deletefree )。

内存分配和释放速度:栈上的内存分配和释放是⾃动的,速度较快。⽽堆上的内存分配和释放需要⼿动操作,速度相对较慢。

new和malloc的区别

类型安全

  • new是C++的运算符,可以为对象分配内存并调⽤相应的构造函数。
  • malloc是C语⾔库函数,只分配指定⼤⼩的内存块,不会调⽤构造函数。

返回类型

  • new返回的是具体类型的指针,⽽且不需要进⾏类型转换。
  • malloc 返回的是 void* ,需要进⾏类型转换,因为它不知道所分配内存的⽤途。

释放内存的方式

  • new的对象用delete,其会调⽤对象的析构函数,然后释放内存。
  • malloc的对象用free,其会调⽤对象的析构函数,然后释放内存

内存分配失败时的⾏为:

  • new在内存分配失败时会抛出 std::bad_alloc
  • malloc 在内存分配失败时返回 NULL 。

内存块⼤⼩:

  • new可以⽤于动态分配数组,并知道数组⼤⼩。
  • malloc 只是分配指定⼤⼩的内存块,不了解所分配内存块的具体⽤途。

delete和free有什么区别

类型安全性

  • delete会调用对象的析构函数,确保资源被正确释放
  • free不了解对象的构造和析构,只是简单得释放内存块

内存块释放后的行为

  • delete释放的内存块的指针指会被设置为nullptr,以避免野指针
  • free不会修改指针的值,可能导致野指针问题

数组的释放

  • delete可以正确释放通过new[]分配的数组
  • free不会修改数组的大小,不适用于释放通过malloc分配的数组。

野指针

野指针(wild pointer)是指指向内存中未知位置的指针,即指针变量指向的地址没有被正确初始化或者指向的内存已经被释放,但指针变量仍然保留了这个地址。这样的指针通常会导致未定义行为,可能会导致程序崩溃、数据损坏或者其他不可预测的结果。

导致野指针的原因

未初始化的指针

当指针变量被声明但未被初始化时,其值是未知的,它可能指向任意内存地址。

int *ptr; // 未初始化的指针

已释放或者销毁的指针

当指针变量指向的内存已经被释放或销毁,但指针变量本身仍然保留了之前的地址。

int *ptr = new int; // 为指针分配内存
delete ptr; // 释放指针所指向的内存
//此时 ptr 成为野指针,因为它仍然指向已经被释放的内存
ptr = nullptr; // 避免野指针,应该将指针置为 nullptr 或赋予新的有效地址

返回局部变量的指针

int* createInt()
{int x = 10;return &x;
}

如何避免野指针

  • 初始化指针:始终确保在使用指针之前将其初始化为一个有效的地址或者 nullptr。

  • 及时置空指针:当指针所指向的内存不再需要时,及时将指针置为空指针(nullptr)。

int *ptr = new int; // 为指针分配内存
delete ptr; // 释放指针所指向的内存
//此时 ptr 成为野指针,因为它仍然指向已经被释放的内存
ptr = nullptr; // 避免野指针,应该将指针置为 nullptr 或赋予新的有效地址
  • 避免返回局部变量的指针
  • 注意函数参数的⽣命周期, 避免在函数内释放调⽤⽅传递的指针
void foo(int*& ptr) { // 操作 ptr delete ptr; // 这⾥可能造成调⽤⽅的指针成为野指针 ptr = nullptr; 
} 
int main() { int* ptr = new int; foo(ptr); 
}
  • 使用智能指针:使用C++11引入的智能指针(如std::unique_ptrstd::shared_ptr等),它们可以自动管理内存生命周期,避免了手动管理内存带来的潜在问题,从而减少了野指针的风险。

野指针和悬浮指针

区别在于,野指针可能指向任意未知或无效的内存地址,而悬空指针则是指向已经被释放或者超出作用域的对象的地址悬空指针通常是由于对已释放对象的指针未及时置空而产生的,而野指针则可能是由于未初始化、错误释放或者错误使用指针而产生的。虽然两者都可能导致程序出错,但野指针的来源更加广泛,也更容易产生严重的后果。
以下为悬空指针的典型案例:

int *ptr;
{int value = 10;ptr = &value; // 指针指向局部变量value,当value超出作用域后,ptr就成为悬空指针
} // value超出作用域,指针ptr成为悬空指针

内存对齐

什么是内存对其

元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小(通常它为4或8)来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始,这就是所谓的内存对齐。

为什么要内存对齐

引用的一位B友(L__B_)的评论:
一般来说,64位机器对应8个字,也就是每次取八个字节,而且由于内存的分层级策略,直接和CPU打交道的是CPUcache,而一个cacheline一般是32或64个字节。完全不对齐的话,CPU读取一个数据的寻址次数将大大增加,甚至有些CPU如果你不照着8字节或者4字节对齐直接g,对齐的话能够保证更少的cache miss,加快CPU处理速度。

内存对其的规则

仍然来自B友(L__B_)的评论
64位系统对应8字节,为了减少存取次数,数据成员应该以8字节为单位存取,保证中途不会跨越式的拿到其他不完整的成员数据,比如int char int,从头到尾依次字节数为4 1 4,那么八个字节取出发现会拿到第三个成员的不完整数据,所以我们给第二个成员+padding到4字节所以总共这个结构体就是12字节。同样我们假设上述的结构体类型为t,那么有下列结构 int64_t int64_t t int64_t int64_t,8 8 12 8 8。以8字节遍历,发现在第四次遍历的时候拿到了其他成员的数据,所以给这12这个结构+padding到16,所以总共48字节。32位默认4字节也是同理。

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

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

相关文章

Word字号与磅值与行距

文章目录 简介字号、磅值与行距之间的对应关系 简介 在论文等文字资料进行排版的过程中,悉知字号对应的磅值,以及行距之间的关系非常有帮助。本篇博文简单介绍一下排版领域中有关中文字号(三号、小四、五号、小五等)与磅值&#…

Redis 教程系列之Redis 分区(十)

Redis 分区 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。 分区的优势 通过利用多台计算机内存的和值,允许我们构造更大的数据库。通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和…

A Novel Negative Sample Generating Method for KnowledgeGraph Embedding

摘要 为了有效地提取知识图中的关系和原因,将实体和关系编码到一个连续的低维语义空间中。在负样本生成阶段,大多数知识图嵌入方法更注重替换头或尾实体以提高训练效率,很少替换关系。这些负样本生成方法对关系预测的贡献不大。本文提出了一…

vue项目在本地源码方式启动和打包之后在nginx中代理有什么不同

Vue项目在本地源码方式启动和打包之后在Nginx中代理的主要区别在于开发环境与生产环境的配置、性能优化、安全性和部署流程等方面。以下是一些具体的差异点: 开发环境与生产环境: 本地源码启动通常是在开发环境中,使用Vue CLI的vue-cli-servi…

有效沟通(业务分析关键技能)

背景 业务分析有一筐子技能,如果让我选一个最重要的,那么就是如何有效沟通,这也可以从项目的另外一个角度思考,项目如何保障能安全着陆,那就是别走偏了。 走偏了,大多数就是对项目理解不清楚,…

智能网络运维:领航数字时代,实现网络管理极致效能

在当今高度信息化的时代,网络已经成为企业运营不可或缺的一部分。网络设备的稳定运行、数据传输的畅通无阻,都直接关系到企业的正常运营和业务发展。因此,高效、智能的网络运维管理显得尤为重要。本文将重点介绍智能网络运维中的几个关键模块…

更新对象的部分输入参数

更新对象的部分输入参数 代码 def update_state(self, **kwargs):# 更新指定的状态参数,保持其他参数不变for key, value in kwargs.items():if hasattr(self, key):setattr(self, key, value)怎么理解解释 用于更新对象的状态参数。这个方法使用了关键字参数&am…

8、Spring CLI中AI命令指南

AI 命令指南 OpenAI 的 ChatGPT 等大型语言模型为使用 AI 生成代码提供了强大的解决方案。ChatGPT 不仅在 Java 代码上进行了训练,还在 Spring 开源生态系统内的各种项目上进行了训练。这使得 Spring CLI 能够增强应用程序的功能,超出传统教程所能提供的范围。 使用简单的命…

每天学习一会java(第一天)----条件运算符

今天学习的是条件运算符 1.描述: 条件运算符由“?”与 “:” 两个符号组成,必须一起使用,是 JAVA 中唯一的三目(三元)运算符,需要三个操作数才能进行运算。 条件表达式的一般使用形式为: 表达…

瑞吉外卖实战学习--登录过滤器和判断是否登录过

完善登录功能 1、创建自定义过滤器LoginCheckFiler1.1通过WebFilter创建过滤器1.2 验证是否可以拦截请求1.3 代码 2、在启动类加入注解ServletComponentScan 用来扫描过滤器触发所有的过滤器ServletComponentScan 3、完善过滤器的处理逻辑3.1判断是否需要是要放行的请求3.2判断…

鸿蒙OS应用示例:【数字滚动计时】

实现效果: 代码示例: RollingText.ets 组件封装 RollingText.ets 组件封装 /*** 滚动文字特效*/ Component export default struct RollingText {private num:numberprivate timerId: number -1State counter: number 0aboutToAppear() {this.timerId…

Git基础(25):Cherry Pick合并指定commit id的提交

文章目录 前言指定commit id合并使用TortoiseGit执行cherry-pick命令 前言 开发中,我们会存在多个分支开发的情况,比如dev,test, prod分支,dev分支在开发新功能,prod作为生产分支已发布。如果某个时候,我们…

3.26C++

定义一个矩形类(Rectangle),包含私有成员:长(length)、宽(width), 定义成员函数: 设置长度:void set_l(int l) 设置宽度:void set_w(int w) 获取长度:int…

【Linux】线程同步{死锁/线程同步相关接口/由浅入深理解线程同步}

文章目录 1.死锁1.1概念1.2死锁的必要条件 2.线程同步相关接口2.1pthread_cond_init/destroy()2.2int pthread_cond_wait2. 3linux下的条件变量及其作用2.4int pthread_cond_signal/broadcast();2.5Linux下 阻塞和挂起的异同2.6阻塞,挂起,和进程切换的关…

【MySQL】数据库--基础

目录 一、概念: 二、连接数据库[Dos命令] 三、SQL 语句分类 一、概念: MySQL 是一种开源的关系数据库管理系统 (RDBMS)数据库-表的本质仍然是文件 二、连接数据库[Dos命令] mysql -h:mysql服务的主机(默认连接到本机服务器&…

轻松掌握:使用 API 接口自动缩短网址的秘诀

在互联网的世界里,网址缩短已经成为了一种时尚和必要。长而复杂的网址不仅难以记忆,还可能让人望而却步。但是,现在有了 API 接口,我们可以轻松地将网址自动缩短,让分享变得更加简单和高效!本文将以具体例子…

自增不再简单:深入探索MySQL自增ID的持久化之道

概述 MySQL中的自增特性估计大家或多或少都是用过。一张表中只能由一个自增字段,通常我们会把它设置为主键,但是随着大家系统越来越分布式,为了一些性能和可扩展性问题,大家目前选择更多的都是分布式ID(雪花算法、UUI…

【python】Jupyter Notebook 修改默认路径

文章目录 一、修改前(一)问题(二)修改前的默认路径 二、修改配置文件、更改路径(一)找到配置文件并打开(二)创建目标文件夹、得到新的路径(三)修改配置文件 三…

运行conda activate报错,有关提示运行conda init...

由于刚配置了anaconda环境变量,打开cmd输入环境激活命令 oonda activate报错,提示要先初始化 在cmd命令行界面输入初始化命令后,在同一界面再次输入conda activate仍提示错误 conda init解决方案:在初始化后,需要关闭…

大模型时代的向量数据库:原理解析和应用案例

大家好,在人工智能领域,数据处理和加工的需求愈发增加。随着人们深入探索AI高级的应用,如图像识别、语音搜索和推荐引擎等,数据的复杂性也在不断地增加。此时传统的数据库存储方式已不能完全满足需求,向量数据库应运而…