C++有关内存的那些事

打印 上一主题 下一主题

主题 1779|帖子 1779|积分 5337


   
个人主页: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++中的一个标准库函数,用来实现内存拷贝操作。它的原型如下:
  1. void *memcpy(void *dest, const void *src, size_t n);
复制代码
在C++中,也可以使用内存拷贝操作来复制数组元素。C++提供了memcpy函数,它与C的memcpy函数功能雷同,但被包含在std命名空间中。
下面是使用memcpy实现数组元素拷贝的示例代码:
  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. int main() {
  5.     int srcArray[] = {1, 2, 3, 4, 5};
  6.     int destArray[5];
  7.     // 使用memcpy函数拷贝数组元素
  8.     memcpy(destArray, srcArray, sizeof(srcArray));// 思考:sizeof(srcArray)是什么?这个函数拷贝拷贝了多少元素?
  9.     memcpy(destArray, srcArray,20);//思考:这里可以拷贝多少元素?
  10.     //思考:这里是谁拷贝的谁?
  11.     // 打印目标数组的元素
  12.     for (int i = 0; i < sizeof(destArray) / sizeof(destArray[0]); i++) {
  13.         cout << destArray[i] << " ";
  14.     }
  15.    
  16.     return 0;
  17. }
复制代码
  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是可以处理内存块的重叠的。它的函数原型为:
  1. void *memmove(void *dest, const void *src, size_t count);
复制代码
拷贝数组:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main()
  4. {
  5. int arr1[] = { 1,2,3,4,5,6,7,8,9,10};
  6. memmove(arr1+2, arr1, 20);
  7. for (int i = 0; i < 10; i++)
  8. {
  9.      cout<<arr1[i]<<' ';
  10. }
  11. return 0;
  12. }
复制代码
输出结果:

   这里出现了一个问题,那就是内存重叠了。
  那么,我们如那里理这个内存重叠问题呢?
   我们可以检查它们的内存是否重叠。
  假如是,那我们就直接从后往前拷贝,否则,我们就从前今后拷贝。
  图示:

   src:src 是 memory source 的缩写,体现源地点,即需要被复制的内存块的起始位置。
  dest:dest 是 destination 的缩写,体现目的地点,即复制后的内存块的起始位置。
  代码:
  1. // 自定义 memmove 函数,解决内存重叠问题
  2. void* me(void* d, const void* sr, size_t n) {
  3.     void* ret = d;
  4.     if (d <= sr || (char*)d >= ((char*)sr + n)) {
  5.         // 从前往后
  6.         while (n--) {
  7.             *(char*)d = *(char*)sr;
  8.             d = (char*)d + 1;
  9.             sr = (char*)sr + 1;
  10.         }
  11.     } else {
  12.         // 从后往前
  13.         d = (char*)d + n - 1;
  14.         sr = (char*)sr + n - 1;
  15.         while (n--) {
  16.             *(char*)d = *(char*)sr;
  17.             d = (char*)d - 1;
  18.             sr = (char*)sr - 1;
  19.         }
  20.     }
  21.    
  22.     return ret;
  23. }
复制代码
1.3 memset函数

memset函数式将指定巨细的内存块设置为给定的值。它的函数原型为:
  1. void * memset ( void * ptr, int value, size_t num);
复制代码
使用:
  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. int main() {
  5.     char str[10];
  6.    
  7.     // 将str的前5个字节设置为字符 'A'
  8.     memset(str, 'A', 5);
  9.    
  10.     cout << str << endl;  // 输出 "AAAAA"
  11.    
  12.     return 0;
  13. }
复制代码
运行结果:

2.各数据类型占用

我们可以用sizeof(数据类型)格式来盘算个数据类型的占用内存巨细。这里我们要相识一个知识点:
   字节是盘算机中的最小存储单位,通常用来体现一个字母、一个数字大概一个符号。一个字节即是8个二进制位,即8个0或1。字节是盘算机中信息存储和传输的基本单位,用来体现各种数据类型和文件巨细。
    二进制位是盘算机中的最小计数单位,用于体现数字的最基本形式。二进制位只能是0或1两种状态,用于体现八进制、十进制、十六进制等不同进制数体系的数值。盘算机中的所有数据都是以二进制位的形式存储和处理的,二进制位的组合可以体现各种不同的数值和字符。8个二进制位组合在一起形成一个字节,即8位二进制位体现一个字节的数据。
  2.1bool类型

代码:
  1. cout<<sizeof(bool)<<endl;
复制代码
占用一字节。
解释:
   bool类型占用内存是一个字节。虽然各人可能觉得bool类型的取值范围只有true和false两种,占用内存应该很小,但是为了在内存中存储和处理bool类型的值,需要用一个字节来体现。这是由于盘算机在内存中最小的存储单元就是一个字节,无法将一个布尔值存储在更小的存储单元中。因此,无论bool类型的值占用的现实位数是多少,它始终会占用1个字节的内存空间。
  2.2char类型

代码:
  1. 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类型:
  1. cout<<sizeof(short)<<endl;
复制代码
占用二字节,也就是十六位二进制位,可以体现2^16=65536个值。
int类型:
  1. cout<<sizeof(int)<<endl;
复制代码
占用四字节,三十二位二进制位,可以体现2^32=4294967296个值。
long类型:
  1. cout<<sizeof(long)<<endl;
复制代码
也是占用四字节,体现三十二位二进制位,可以体现2^32=4294967296个值,和int类型一样。
整数:
整数的存储都是由原码、反码、补码来体现的:
   对于整数:
  1. 原码:只要通过正负数判断即可获得原码,(正数1,负数0)
  2. 反码:在原码的根本上,对负数的各位取反
  3. 补码:在反码的根本上,对负数的最低有效位加1。即将反码(符号位除外)加1得到补码。
  注:正整数的原、反、补码是雷同的。
  在盘算机内存中,整数通常用二进制补码的形式来储存。为什么呢?
   由于盘算机中通过补码运算可以实现加法、减法、乘法和除法等操作。在进行运算时,盘算机会自动进行补码的转换和处理。
  2.4float类型及double类型及浮点数

由于float和double都是储存浮点数的,以是归为一类了。
float类型
代码:
  1. cout<<sizeof(float)<<endl;
复制代码
占用四字节,三十二位二进制位。
这里需要相识一个体现方式,就是二进制的科学体现法:
  1. ± mantissa × 2 exponent
  2. (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类型
代码:
  1. 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转化为二进制的过程如下:
    
  1. ​留下的小数部分   积    整数部分
  2.    0.375 x 2 = 0.75 -> 0//整数部分为0,记录整数部分,小数部分继续乘2
  3.    0.75 x 2 = 1.5 -> 1//整数部分为1,记录整数部分,小数部分继续乘2
  4.    0.5 x 2 = 1.0 -> 1//整数部分为1,记录整数部分,小数部分为0,停止运算
复制代码
以是,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一个免费的赞和关注,谢谢各人啦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表