IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> [详解C语言]一文带你深入了解数据在内存中的存储 -> 正文阅读

[C++知识库][详解C语言]一文带你深入了解数据在内存中的存储

?作者简介:大家好我是zoroxs
📃个人主页:c/c++学习之路
🔥💖如果觉得博主的文章还不错的话,请👍三连支持一下博主哦

1??内存简单介绍

大家肯定经常听说内存这个词,内存到底是什么呢? 在计算机中,进程都要加载进内存中,也是我们各种数据的流通途径,C语言中,大家肯定都知道指针变量,指针变量中保存的就是内存的地址,那么,什么是内存的地址呢?

  • 内存的单位是字节

对于32位的机器,有32根地址线,每根地址线在寻址时,产生的高低电压分别为0/1,那么32根地址线产生的地址就会是

00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010

11111111111111111111111111111111

这里就有2^32次方个地址
大家应该知道,还有64位的机器,64根地址线又有多少个地址呢,大家可以计算一下

在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,
所以一个指针变量的大小就应该是4个字节。
那如果在64位机器上,有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

这串编号就是内存单元的地址,就像酒店的房间号一样,对应着内存中的一个字节大小的房间
我们在vs中来看一下
image.png
这里是以十六进制的方式展示的,大家也知道,32个数字看起来太长了。

2??整数+字符在内存中的存储

关于c语言中的数据类型,大家在写了这么多代码后肯定也很清楚了,C语言中有整型、浮点型、字符型、等等
我们来研究一下整数在内存中是如何存储的
大家都知道,定义变量,会在内存中开辟空间来存储

int a = 20;

int类型在vs中占据4个字节的空间,那么如何存储呢?
这就涉及到原码反码补码的概念

  • 计算机中的整数有三种2进制表示方法,即原码、反码和补码。
  • 三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,剩下数值位
  • 正数的原、反、补码都相同。
  • 负整数的三种表示方法各不相同。

原码:

直接将数值按照正负数的形式翻译成二进制就可以得到原码

反码:

将原码的符号位不变,其他位依次按位取反就可以得到反码

补码:

反码+1就得到补码。

对于整型变量来说,内存中存放的其实是补码
使用补码,可以将符号位和数值域统一处理,加法和减法也可以同一处理,因为CPU只有加法器
eg:

int a = 20;
//原码 : 直接写二进制    00000000000000000000000000010100
//反码--补码  -- 正数的原反补相同

int  b =  -5;
//原码:   10000000000000000000000000000101
//反码:符号位不变,按位取反
//        11111111111111111111111111111010
//补码:反码+1
//        11111111111111111111111111111011  --   -5的补码

上边可以看见-5的补码是11111111111111111111111111111011 ,我们如何确认呢?
转换成16进制为fffffffb
image.png
大家可以看到确实是使用补码存储的,但是为什么是倒着存储的,后边再来说,这是由于大小端的问题
给大家举几个例子,不知道存储无法分析出结果代码

//1.
#include <stdio.h>
int main()
{
  char a = -128;
  printf("%u\n",a);
  //-128 原码:  10000000 00000000 00000000 10000000
  //-128 反码:  11111111 11111111 11111111 01111111
  //-128 补码:  11111111 11111111 11111111 10000000

  //存在a里面的,因为只有一个字节   10000000
  //所以会当做无符号整数打印 --整形提升
  //提升为  11111111111111111111111110000000
    
  //所以结果是一个很大的整数,大家可以尝试一下
  return 0;
}

image.png

#include <stdio.h>
int main()
{
  char a = 128;
  //128的原码反码补码 : 00000000000000000000000010000000
  //存在a里的:10000000
  //整形提升 :11111111111111111111111110000000
  //所以结果还是那个很大的整数
  printf("%u\n",a);
  return 0;

例子就简单给大家举到这里,大家一定要记住整数在内存中是以补码的形式存储的
字符存储的是ASCII码,所以和整数同

3?? 浮点数在内存中的存储

我们来看一下浮点数在内存中的存储
抛砖引玉:

#include <stdio.h>
int main()
{
    int n = 9;
    float *pFloat = (float *)&n;
    printf("n的值为:%d\n",n);
    printf("*pFloat的值为:%f\n",*pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n",n);
    printf("*pFloat的值为:%f\n",*pFloat);
    return 0;
}

上面代码的打印结果到底是什么呢?
image.png
是不是非常出乎大家的意料呢,这里就可以看出,浮点数的存储肯定和整数是不同的。那浮点数到底咋存的呢?
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

  • (-1)^S * M * 2^E
  • (-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
  • M表示有效数字,大于等于1,小于2。
  • 2^E表示指数位。

看得很迷糊,直接上例子

v = 5.5
= 101.1 --二进制表示
= 1.011 * 2 ^2 – 科学记数法表示
因为是正数 s = 0
m = 1.011
e = 2

image.png
IEEE 754规定:
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
image.png
image.png
IEEE 754对有效数字M和指数E,还有一些特别规定
1=<M <2 ,所以M可以写成1.xxxxx 所以可以舍去1 ,只存储xxxxxx

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字

对于指数E,E是一个无符号整数,但是科学记数法指数是可以出现负数的,所以
IEEE 754规定了偏移量,如果E为8位,则加上127 ,如果E为11位,则加上1023
我们举个例子

float f = 8.5f;
//二进制 : 1001.1
//科学计数法表示: 1.0011*2^3
//S = 0 M = 1.0011  E = 3
//存储进去的应该是:
    0 10000010 00110000000000000000000
//我们可以验证一下
        转换成16进制
 0100 0001 0001 1000 0000 0000 0000 0000
//41 18 00 00

我们来看一下代码

image.png

和我们想的一样
我们再来举一个负数的例子

float t = -3.5f;
//二进制: 11.1
//科学记数法: 1.11*2^1
//S = 1 M = 1.11 E = 1
//存储:
1  10000000 11000000000000000000000
//转换成16进制
1100 0000 0011 0000 0000 0000 0000 0000
c0 60 00 00

image.png
那我们从内存中读取出来的二进制位如何解析成浮点数呢
关于E,有三种情况

  1. E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1

2.E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于
0的很小的数字。

3.E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);

我们来分析一下最开始的题目
image.png
浮点数和整数的存储就介绍到这里了,有哪里不清楚的朋友可以私信我

4??大小端存储模式及简介

上边有一个悬念,为什么是倒序存储的
image.png
那什么是大端小端呢?

大端(存储)模式: 数据的低位存储在内存的高地址中,数据的高位存储在内存的低地址中
小端(存储)模式: 数据的低位存储在内存的低地址中,数据的高位存储在内存的高地址中

那为什么会有大小端呢?
内存中以字节为单位,但是比如int 是4个字节,那如何安排这个4个字节呢?就导致了大小端存储模式

例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为
高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高
地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则
为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式
还是小端模式。

那我们如何测试当前电脑是哪种存储模式呢?

int main(void)
{
	int a = 0x11223344;

	return 0;
}

我们调试打开内存看一下
image.png
很明显,数据的低位存储在内存低地址中,所以为小端存储模式。
我们能不能写一个程序,直接告诉我们大小端呢?
我们来分析一下
image.png
我们来看一下代码

int decide_byte_orde()
{
    int i = 1;
    return *(char *)&i;
}

我们来测试一下
image.png

5??总结

深入理解数据的存储是非常有必要的,我们之后碰到很多问题都会豁然开朗,大家一定要好好研究一下

🔥💖如果觉得博主的文章还不错的话,请👍三连支持一下博主哦

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:46:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:36:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码