C++有关内存的那些事

news2025/2/27 14:28:06

个人主页:PingdiGuo_guo
收录转栏:C++干货专栏

前言

本篇博客是讲解关于C++内存的一些知识点的。

文章目录

前言

1.内存函数

1.1memcpy函数

1.2memmove函数

1.3 memset函数

2.各数据类型占用

2.1bool类型

2.2char类型

2.3short、int、long类型及整数

2.4float类型及double类型及浮点数

3.学习内存有什么用

总结


1.内存函数

内存函数是在计算机程序中用来操作内存的一类函数。内存函数可以用于分配和释放内存,读取和写入内存中的数据,以及进行内存的复制和移动等操作。

在这里,我们主要介绍几种可以复制的内存函数。

1.1memcpy函数

memcpy函数是C++中的一个标准库函数,用来实现内存拷贝操作。它的原型如下:

void *memcpy(void *dest, const void *src, size_t n);

在C++中,也可以使用内存拷贝操作来复制数组元素。C++提供了memcpy函数,它与C的memcpy函数功能相同,但被包含在std命名空间中。

下面是使用memcpy实现数组元素拷贝的示例代码:

#include <iostream>
#include <cstring>

using namespace std;

int main() {
    int srcArray[] = {1, 2, 3, 4, 5};
    int destArray[5];

    // 使用memcpy函数拷贝数组元素
    memcpy(destArray, srcArray, sizeof(srcArray));// 思考:sizeof(srcArray)是什么?这个函数拷贝拷贝了多少元素?
    memcpy(destArray, srcArray,20);//思考:这里可以拷贝多少元素?
    //思考:这里是谁拷贝的谁?

    // 打印目标数组的元素
    for (int i = 0; i < sizeof(destArray) / sizeof(destArray[0]); i++) {
        cout << destArray[i] << " ";
    }
    
    return 0;
}

1.sizeof(srcArray)是srcArray数组的字节大小,即sizeof(int) * 5,所以sizeof(srcArray)是20。

2.这个函数拷贝了5个元素。因为memcpy函数根据参数指定的字节数进行拷贝,sizeof(srcArray)指定了srcArray数组的字节大小,所以拷贝了整个srcArray数组的元素。

3.第二个memcpy函数使用了20作为拷贝的字节数。因为是5每个int占4个字节,所以这个函数可以拷贝5个元素。

4.这里是destArray拷贝了srcArray数组。

memcpy函数在处理内存重叠问题时是未定义行为。也就是说,如果源内存块和目标内存块重叠,memcpy函数可能会导致不可预测的结果。

输出结果:

1.2memmove函数

memmove与memcpy类似,但不同的地方是memmove是可以处理内存块的重叠的。它的函数原型为:

void *memmove(void *dest, const void *src, size_t count);

拷贝数组:

#include <bits/stdc++.h>
using namespace std;
int main()
{
 int arr1[] = { 1,2,3,4,5,6,7,8,9,10};
 memmove(arr1+2, arr1, 20);
 for (int i = 0; i < 10; i++)
 {
     cout<<arr1[i]<<' ';
 }
 return 0;
}

输出结果:

这里出现了一个问题,那就是内存重叠了。

那么,我们如何处理这个内存重叠问题呢?

我们可以检查它们的内存是否重叠。

如果是,那我们就直接从后往前拷贝,否则,我们就从前往后拷贝。

图示:

src:src 是 memory source 的缩写,表示源地址,即需要被复制的内存块的起始位置。

dest:dest 是 destination 的缩写,表示目标地址,即复制后的内存块的起始位置。

代码:

// 自定义 memmove 函数,解决内存重叠问题
void* me(void* d, const void* sr, size_t n) {
    void* ret = d;

    if (d <= sr || (char*)d >= ((char*)sr + n)) {
        // 从前往后
        while (n--) {
            *(char*)d = *(char*)sr;
            d = (char*)d + 1;
            sr = (char*)sr + 1;
        }
    } else {
        // 从后往前
        d = (char*)d + n - 1;
        sr = (char*)sr + n - 1;
        while (n--) {
            *(char*)d = *(char*)sr;
            d = (char*)d - 1;
            sr = (char*)sr - 1;
        }
    }
    
    return ret;
}

1.3 memset函数

memset函数式将指定大小的内存块设置为给定的值。它的函数原型为:

void * memset ( void * ptr, int value, size_t num);

使用:

#include <iostream>
#include <cstring>
using namespace std;
int main() {
    char str[10];
    
    // 将str的前5个字节设置为字符 'A'
    memset(str, 'A', 5);
    
    cout << str << endl;  // 输出 "AAAAA"
    
    return 0;
}

运行结果:

2.各数据类型占用

我们可以用sizeof(数据类型)格式来计算个数据类型的占用内存大小。这里我们要了解一个知识点:

字节是计算机中的最小存储单位,通常用来表示一个字母、一个数字或者一个符号。一个字节等于8个二进制位,即8个0或1。字节是计算机中信息存储和传输的基本单位,用来表示各种数据类型和文件大小。

二进制位是计算机中的最小计数单位,用于表示数字的最基本形式。二进制位只能是0或1两种状态,用于表示八进制、十进制、十六进制等不同进制数系统的数值。计算机中的所有数据都是以二进制位的形式存储和处理的,二进制位的组合可以表示各种不同的数值和字符。8个二进制位组合在一起形成一个字节,即8位二进制位表示一个字节的数据。

2.1bool类型

代码:

cout<<sizeof(bool)<<endl;

占用一字节。

解释:

bool类型占用内存是一个字节。虽然大家可能觉得bool类型的取值范围只有true和false两种,占用内存应该很小,但是为了在内存中存储和处理bool类型的值,需要用一个字节来表示。这是因为计算机在内存中最小的存储单元就是一个字节,无法将一个布尔值存储在更小的存储单元中。因此,无论bool类型的值占用的实际位数是多少,它始终会占用1个字节的内存空间。

2.2char类型

代码:

cout<<sizeof(char)<<endl;

占用一个字节,及八位二进制位,图表:

1/01/01/01/01/01/01/01/0

其中,每一个二进制位的变化都可以表示一个不同的值,也就是2^8=256个值,只是当有符号和无符号时表示的范围并不相同,我们平时的所用的每一个字符在内存中都由8位2进制位来表示。

比如,字符'A'在ASCLL码中对应65,在内存则表示为:

01000001

2.3short、int、long类型及整数

short、int、long类型都是储存整数的,所以放到一块讲了。

short类型:

cout<<sizeof(short)<<endl;

占用二字节,也就是十六位二进制位,可以表示2^16=65536个值。

int类型:

cout<<sizeof(int)<<endl;

占用四字节,三十二位二进制位,可以表示2^32=4294967296个值。

long类型:

cout<<sizeof(long)<<endl;

也是占用四字节,表示三十二位二进制位,可以表示2^32=4294967296个值,和int类型一样。

整数:

整数的存储都是由原码、反码、补码来表示的:

对于整数:

1. 原码:只要通过正负数判断即可获得原码,(正数1,负数0)

2. 反码:在原码的基础上,对负数的各位取反

3. 补码:在反码的基础上,对负数的最低有效位加1。即将反码(符号位除外)加1得到补码。

注:正整数的原、反、补码是相同的。

在计算机内存中,整数通常用二进制补码的形式来储存。为什么呢?

因为计算机中通过补码运算可以实现加法、减法、乘法和除法等操作。在进行运算时,计算机会自动进行补码的转换和处理。

2.4float类型及double类型及浮点数

因为float和double都是储存浮点数的,所以归为一类了。

float类型

代码:

cout<<sizeof(float)<<endl;

占用四字节,三十二位二进制位。

这里需要了解一个表示方式,就是二进制的科学表示法:

± mantissa × 2 exponent
(mantissa:尾数,exponent:指数,均使用二进制表示)

它的储存采用了IEEE 754单精度浮点格式,存储方式如下:

1 bit(符号位)

8 bit(指数位)

23 bit(尾数位)

00  1  1  1  1  1  0  0  00  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
3031                              2322                                                                                         0

这里的bit也是二进制位,是二进制位的缩写。如上图所示,该格式最高一位是符号位,0位正,1位负,后面8位为无符号整型数,表示范围为0~265,后面23位小数为,索引从22到0分别对应2^-1到2^-23。

double类型

代码:

cout<<sizeof(double)<<endl;

double型在内存中有八个字节,储存的数据较大,六十四位二进制位(bit)。他和float类型一样,都是采用的二进制的科学计数法。

它的储存采用了IEEE 754双精度浮点格式,储存方式如下:

1 bit(符号位)

11 bit(指数位)

52 bit(尾数位)

第一位为63倒数第一位为52

倒数第一位为0

如上图所示,该格式最高位也为符号位,0位正,1位负,后面11位为无符号整型数,表示范围为0~2^11-1,后面52位小数为,索引从51到0分别对应2^-1到2^-52。

浮点数

浮点数的储存方式可看上面的两张图表,接下来讲一下浮点数如何转化为二进制。

步骤:

1. 将浮点数分为整数部分和小数部分。例如,考虑浮点数12.375,整数部分为12,小数部分为0.375。

2. 将整数部分转化为二进制。对于整数部分,可以使用短除法将其转化为二进制。例如,12转化为二进制是1100。对于短除法,这里就不过多讲述了,大家可以去查一查。

3. 将小数部分转化为二进制。对于小数部分,可以使用乘2取整法将其转化为二进制。将小数部分乘以2,取整数部分,然后将小数部分的整数部分再乘以2,依此类推,直到小数部分为0或达到所需的精度。例如,0.375转化为二进制的过程如下:

  

   0.375 x 2 = 0.75 -> 0//整数部分为0,继续乘2
   0.75 x 2 = 1.5 -> 1//整数部分为1,舍弃整数部分,继续乘2
   0.5 x 2 = 1.0 -> 1//乘得积为1,停止运算

    所以,0.375转化为二进制是0.011。

4. 合并整数部分和小数部分的二进制。将步骤2和步骤3得到的二进制合并在一起,注意小数点的位置。对于上述例子,合并后的二进制是1100.011。

5.  二进制小数转化为十进制验算。比如,二进制小数1100.011=1*2^2+0*2^2+1*2^2+(0 * 2^-1) + (1 * 2^-2) + (1 * 2^-3) =12.375,正确。

3.学习内存有什么用

大家可能会有一些疑问,学内存知识有什么用呢?学内存有以下几个方面的作用:

1. 内存管理:C++是一种低级语言,需要手动管理内存分配和释放。了解C++内存知识可以帮助我们正确地分配和释放内存,避免内存泄漏和野指针等问题,提高程序的健壮性和效率。

2. 优化性能:理解C++内存模型和内存使用方式可以帮助我们优化程序性能。例如,了解内存对齐和缓存行的概念可以避免访问内存的延迟,提高程序的运行速度。

3. 安全性和稳定性:内存相关的错误往往是导致程序崩溃和漏洞的主要原因之一。学习C++内存知识可以帮助我们避免常见的内存错误,提高程序的安全性和稳定性。

4. 调试和错误排查:当程序出现内存相关的问题时,了解C++内存知识可以帮助我们更快地定位和修复问题,提高调试的效率。

总的来说,学习C++内存知识对于使用和开发C++程序非常重要,可以帮助我们进行内存管理、优化性能、提高安全性和稳定性,深入理解语言特性,以及进行调试和错误排查。

总结

本篇博客到这里就结束了,感谢大家的支持与观看,有好的建议欢迎留言,如果这篇博客对您有帮助,那请给PingdiGuo_guo一个免费的赞,谢谢大家啦!

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

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

相关文章

英语广场杂志英语广场杂志社英语广场编辑部2024年第3期目录

英语翻译理论与实践 浅析钱钟书“化境论”与文言文英译的适配度 冯睿;姚锦宁;李佳彧; 3-6《英语广场》投稿&#xff1a;cn7kantougao163.com 目的论视角下《写作、阅读和演讲的艺术》的翻译分析报告 张俊怡; 7-10 新加坡籍译者温宏文翻译行为研究 周梦; 11-14 …

CVE-2024-24112 XMall后台管理系统 SQL 注入漏洞分析

------作者本科毕业设计项目 基于 Spring Boot Vue 开发而成...... [Affected Component] /item/list /item/listSearch /sys/log /order/list /member/list (need time-based blind injection) /member/list/remove 项目下载地址 Exrick/xmall: 基于SOA架构的分布式…

百年难遇:3款良心好用的国产软件,常被误以为是外国佬开发

许多出色的国产软件&#xff0c;但由于某些特殊原因&#xff0c;我们常常错过了它们。 今天&#xff0c;笔者将向大家推荐三款电脑必备的软件&#xff0c;每一款都堪称神器。 Teamind Teamind是一款功能强大的在线白板工具&#xff0c;支持多人协作&#xff0c;成为众多居家…

收银软件多少钱一套?亿发全面解析超市收银系统软件价位

当超市经营达到一定规模时&#xff0c;商户通常会选择配置收银系统软件&#xff0c;以提升收银效率和为顾客提供更完善的购物体验。然而&#xff0c;人们常问&#xff1a;收银系统软件一套需要多少钱&#xff1f;安装一个收银系统又需要多少费用呢&#xff1f;了解收银系统软件…

JDK1.8安装环境变量配置检测是否安装成功

前言 目前市面上最常用的还是java1.8和Mysql5.7&#xff08;企业&#xff09;2024 安装包 已上传CSDN&#xff0c;审核中 JDK安装细节 注&#xff1a;如果怕麻烦就直接安装在C盘&#xff08;默认&#xff09; 环境变量配置 Win10打开左下角图标点击系统&#xff0c;下滑点…

ET框架新起一个服务及实现服务之间的消息通讯

ET框架是熊猫大大写的双端框架&#xff0c;游戏客户端和服务端都是用C#语言来编写 新起一个服务名比如叫做Activity 1.配置文件StartSceneConfig 2. SceneFactory switch (scene.SceneType) {case SceneType.Activity:break; } 定义SceneType枚举类型 public enum SceneTyp…

计算机网络——物理层(物理传输介质和物理层的设备)

计算机网络——物理层&#xff08;物理传输介质和物理层的设备 物理传输介质导向性传输介质双绞线同轴电缆光纤 非导向性传输介质无线电波多径效应 微波地面微波通信ISM 频段 卫星通信 物理层设备中继器集线器中继器和集线器的区别 我们今天进入物理层的物理传输介质和物理层的…

【原创】手动安装open-webui,非官方docker安装方法,可汉化ui

open-webui是一个为LLMs&#xff08;大型语言模型&#xff09;设计的友好型Web用户界面&#xff0c;支持Ollama和OpenAI兼容的API。它提供了直观的聊天界面、响应式设计、快速响应性能、简易安装、代码语法高亮、Markdown和LaTeX支持、本地RAG集成、Web浏览能力、提示预设支持、…

Vue 中使用事件总线来进行组件间通信($emit()、$on() 和 $off())

使用场景&#xff1a; 上一篇文章中写到的&#xff1a; echarts图表左击显示自定义弹框&#xff0c;右击取消自定义弹框 结构图&#xff1a;&#xff08;removet修改为remove&#xff09; 假设这个echarts图表是子组件B页面中。而父页面A的自定义弹框标签里调用了子组件B&am…

如何使用生成式AI来制定SEO内容策略?

SEO内容策略是一系列旨在优化网站内容以提高其在搜索引擎中排名的行动计划&#xff0c;从而增加网站流量并提升品牌影响力。在当今互联网信息海量的情况下&#xff0c;优秀的SEO内容策略可以帮助您的网站脱颖而出&#xff0c;吸引更多目标用户。 背景知识 SEO&#xff08;搜索…

基于Java中的SSM框架实现任务发布接收系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现任务发布接收系统演示 摘要 在如今的互联网发展的时代&#xff0c;互联网成了时代的主角&#xff0c;是这个时代的超级呈现家。在互联网的大家族中&#xff0c;任务发布永远是一个关键点。因为我们所有人离不开任务的发布与任务的管理&#xff0c;因为…

【经验分享】如何在IDEA中快速学习|审查|复习代码工程?

在IDEA中快速学习或审查工程 必要性 提高代码质量和可维护性熟悉项目结构和逻辑发现优化机会知识共享和传承规范团队实践 常用操作 快速浏览工程结构&#xff0c;类&#xff0c;方法&#xff0c;确定位置&#xff1a;“Structure” 查看类、方法或变量的所有引用位置&#…

Axure RP10汉化版获取:低成本高效率操作!

作为市场份额最高的专业原型设计工具&#xff0c;Axure RP10 毫无疑问&#xff0c;功能的强大性和灵活性也受到许多产品经理和设计师的青睐。许多世界百强公司也在使用Axure进行原型设计 RP10。但对于许多本土设计师来说&#xff0c;Axure RP10 全英语界面和陡峭的学习曲线让人…

mac下 3.6.3 版本 maven

问题 Blocked mirror for repositories: [snapshots (http://xxx/artifactory/gm-maven-vir, default, releasessnapshots)]无法访问 Maven 3.8.1 http 仓库。可能的解决方案: - 检查 Maven settings.xml 是否不包含 http 仓库 - 检查 Maven pom 文件是否不包含 http 仓库 htt…

SpringBoot3整合Elasticsearch8.x之全面保姆级教程

整合ES 环境准备 安装配置ES&#xff1a;https://blog.csdn.net/qq_50864152/article/details/136724528安装配置Kibana&#xff1a;https://blog.csdn.net/qq_50864152/article/details/136727707新建项目&#xff1a;新建名为web的SpringBoot3项目 elasticsearch-java 公…

Xilinx LVDS ISERDESE2

ISERDESE2 7 系列 FPGA 是一款专用的串行到并行转换器,具有特定的时钟和逻辑功能,旨在促进高速源同步应用的实现。该ISERDESE2避免了在FPGA架构中设计解串器时遇到的额外时序复杂性. ISERDESE2功能包括: 1,专用解串器/串行转换器 ISERDESE2解串器可实现高速数据传输,而无需…

C语言 实用调试技巧

我们的博客已经更新到了数据结构&#xff0c;但是当我在深耕数据结构时我发现我在C语言是遗漏了一个重要的东西&#xff0c;那就是C语言的使用调试技巧。这篇博客对数据结构非常重要&#xff0c;请大家耐心观看。 1. 什么是bug&#xff1f; 第一次被发现的导致计算机错误的飞蛾…

TCPIP协议总结

一、TCP的三次握手 TCP连接的建立时&#xff0c;双方需要经过三次握手&#xff0c;而断开连接时&#xff0c;双方需要经过四次分手&#xff0c;那么&#xff0c;其三次握手和四次分手分别做了什么呢&#xff1f;又是如何进行的呢&#xff1f; 通常情况下&#xff0c;建立连接的…

数据可视化看板的进阶之路:山海鲸可视化私有化部署体验

作为一名长期使用山海鲸可视化的资深用户&#xff0c;我在数据可视化看板的制作过程中&#xff0c;深刻感受到了这款软件带来的便捷与高效。今天&#xff0c;我想与大家分享一些我在使用山海鲸可视化制作数据可视化看板时的经验&#xff0c;给对这款产品同样感兴趣的朋友同行一…

使用ansible剧本进行lvm分盘

使用 Ansible 剧本&#xff08;Playbook&#xff09;进行 LVM 分区管理是一种自动化的方式&#xff0c;可以帮助管理员在多台主机上批量管理逻辑卷。 部署环境 3台主机&#xff0c;添加硬盘 ansible-galaxy collection install community.general 联网执行&#xff0c;下…