当前位置:网站首页>深度刨析数据在内存中的存储
深度刨析数据在内存中的存储
2022-07-27 22:20:00 【With Order @!147】
文章目录
简介
本文章重点:
- 数据类型详细介绍
- 整形在内存:原码,反码,补码
- 大小端字节序介绍及判断
- 浮点型在内存中的存储解析
正文开始
一、数据类型的介绍
前面已经学习了基本的内置类型
类型的意义:
- 使用这个类型开辟内存空间的大小(大小决定了使用范围)
- 如何看待内存空间的视角
1.1 类型的基本归类
整形家族:
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
long long
unsigned long long [int]
signed long long [int]
在代码中出现char a = 0; 时,c标准未定义a是有符号还是无符号,取决于编译器,但是其他类型用该方法定义变量时,表示是时有符号类型
**注意:**当变量类型为signed时,数据在内存中存储的最高位为符号位,无其他意义,但是当变量类型为unsigned时,数据在内存中以二进制位存储的最高位就不是符号位了,而是表示2^31
浮点型家族
float
double
只要是表示小数就可以使用浮点型
构造类型(自定义类型)
数组类型
int arr1[5]的数组类型为int [5]
同理int arr2[8]的数组类型为int [8]
只要数组的元素类型或者元素个数发生变化时,数组类型就发生变化
结构体类型
枚举类型
联合类型
指针类型
int* pi;
char* pc;
float* pf;
void* pv;
空类型
void表示空类型(无类型)
通常用于函数的返回类型,函数的参数,指针类型(void *)
二. 整形在内存中的存储
我们已经知道变量的创建要在内存开辟空间,空间的大小时根据不同的类型而决定的,下面谈数据在开辟内存中是怎么存储的
这里又牵扯到了二进制的原码,反码,补码,我们知道整形在内存中是以其二进制补码存储的,
2.1 补码
为什么整形在内存中是以其二进制补码存储的?
在计算机系统中,数值一律用补码来表示和存储.原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器) 此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
以1 - 1举例:
原码符号位不变其他位取反后加1得到补码,同样的
补码符号位不变其他位取反后加1得到原码,二者相互转换运算过程相同
回头再读框内的内容仔细思考会就会发现,补码是一个神奇的东西,向IT前辈们致敬
2的二进制为00000000000000000000000000000010
因为二进制1010即可表示10
所以每四位二进制就可以用一个0到10的数表示,这种方法得到的结果为16进制
即2也可用0x00 00 00 02来表示(0b前缀表示二进制,0前缀为8进制)


2.2 大小端介绍
0x11223344 在内存中的存储方式一般有两种,即
(设从左到右依次为从低地址到高地址)
11 22 33 44 , 或者
44 33 22 11
什么是大端小端
大端(存储)模式,是指数据的低位保存在高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中.
什么是字节序
以字节为单位的顺序
11为最高位字节序,44为最低位字节序
大端[字节序]存储:
把一个数据的高位字节序的内容存放在低地址处,吧低位字节序的内容放在高位地址处,就是大端字节序存储
小端[字节序]存储:
把一个数据的高位字节序的内容存放在高地址处,吧低位字节序的内容放在低位地址处,就是小端字节序存储
字节数大于1的数据都存在大小端字节序存储

2.3 练习
2.3.1 设计代码判断编译器大小端
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main() {
//判断大小端存储:
int a = 1;
/*char* p = (char*)&a;*/
if (*(char*)&a == 0) {
printf("大端");
}
else {
printf("小端\n");
}
system("PAUSE");
return 0;
}
2.3.2
signed char的范围是-128到127,-128在内存中以补码存储时比较特殊,他的补码是10000000,用以往的方法转为原码后为一个九个比特位,超过了一个字节,但是他的补码仍是10000000.
那么unsigned char的取值又是多少呢,上面我们介绍了无符号类型的数字没有符号位,其最高位仍有意义,所以1000000表示128,11111111表示255,因此无符号char类型取值范围为0到255
同理,unsigned short类型:
1000000000000000--------2^15--------32768
1111111111111111--------2^31---------65535
0 ~ 65535
signed short: -32768 ~ 32767
那么问题来了
//输出什么?
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a = %d, b = %d, c = %d", a, b, c);
system("PAUSE");
return 0;

这道题涉及到了截断和整形提升,阶段发生在存数据的时候,截断和大小端没有任何关系,截断时是按照正常二进制顺序进行截断的

2.3.3
//输出多少?
char a = -128;
printf("%u\n", a);;
system("PAUSE");
return 0;

2.3.4
//输出什么
unsigned int i;
for (i = 9; i >= 0; i--) {
printf("%u\n", i);
}
system("PAUSE");
return 0;
死循环

3.3.5
//输出什么
char a[1000];
int i;
for (i = 0; i < 1000; i++) {
a[i] = -1 - i;
}
printf("%d", strlen(a));
system("PAUSE");
return 0;

> 注意:无符号整形相加减的时候结果仍为无符号
关于无符号相减,参看大佬博客https://blog.csdn.net/LightInDarkness/article/details/105582031
三、浮点数在内存中的存储
常见浮点数:
3.14159
1E10(科学计数法1.0*10^10)
浮点数家族包括:float double long double类型
浮点数表示的范围:float.h中定义
整形家族的约束及表示范围:limits.h中定义
3.1.浮点数存储规则
3.1.1 存
根据国际标准IEEE(电器和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
(-1) ^ S * M *2 ^ E
(-1)^S表示符号位,当S = 0. V为正数;当S = 1, V为负数
M表示有效数字,大于等于1,小于2
2^E表示指数位
举个例子:
V = 5.0f
二进制:101.0
= 1.01 * 2 ^ 2(类比10进制科学计数法理解)
= (-1) ^ 0 * 1.01 *2^2
S = 0 M = 1.01 E = 2
V = 9.5f
二进制:1001.1 小数点后的权重为2^(-1),2^(-1)...,切不可写成1001.101
= 1.0011 * 2 ^ 3
= (-1) ^ 0 * 1.0011 * 2 ^ 3
小数点后的权重为2 ^ (-1), 2 ^ (-1) …, 正是因为这个原因,就会使得浮点数小数点后的二进制无法精确表示小数点后的值,造成精度丢失.如 9.6f 小数点后的6就很难用二进制表示,所以浮点数有时无法精确存储
IEEE 754规定:
对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M

对于64位的浮点数,最高的1位是符号位,接着的11位是指数E,剩下的23位是有效数字M

IEEE 754对有效数字M和指数E,还有一些特别规定
IEEE 754 规定,在计算机内部保存M,时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的部分,比如保存1.01时,只保存01,读取时再把1加上去,这样可以节省1位有效数字,以32位浮点数为例,留给M的就会由23位变成24位有效数字
至于指数E,情况就比较复杂
首先E为一个无符号整数
这意味着,如果E为8位,它的取值范围为0~255,如果E为11位,它的取值范围为0 ~2047,但是我们知道,科学计数法中的E是可能出现负数的,比如0.5,所以IEEE 754规定,存入内存时的E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023,比如2^10的E是10,保存成32位浮点数时,必须保存成10+127=137,即10001001
以下给出一个例子
float f = 5.5;
//5.5
//101.1
//1.001*2^2
//s=0 m=1.001 e=2
//根据上面的规定,其二进制应为
//**0100 0000 1011 0000 0000 0000 0000 0000**
system("PAUSE");
return 0;
3.1.2 取
指数E从内存中取出还可以再分成三种情况
E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(1023),得到真实值,再将有效数字M前加上第一位的1
E为全0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值
有效数字M不再加上第一位的1,而是还原为0.XXXXXX的小数,这样做是为了表示接近于0的很小的数字
E全为1
这时,如果有效数字M全为0,表示无穷大(正负取决于符号位s)
;

边栏推荐
- 红队大杀器 Behinder_v4.0(冰蝎4.0)
- 单片机之led、数码管与按键
- 立即报名 | 云原生技术交流 Meetup 广州站已开启,8 月 6 号与你相遇!
- Postman download and use tutorial
- LeetCode - 寻找两个正序数组的中位数
- Point divide and conquer analysis
- 投资80亿!南京华天封测一期项目即将投产!
- How to smoothly go online after MySQL table splitting?
- Jerry caused other messages to accumulate in the message pool [article]
- R language evaluates the relative importance of the predictive factors (variables, characteristics) of the regression model, scales the predictive variables of the regression model, and then construct
猜你喜欢
随机推荐
投资80亿!南京华天封测一期项目即将投产!
SRv6初登场
多线程及多线程程序的编写
分支和循环语句题目练习
mysql数据库的基本操作(二)-——基于数据表
单片机之led、数码管与按键
Leetcode:1997. the first day after visiting all rooms [jump DP]
Jerry's PWM setting and PWM IO selection [chapter]
Leetcode 452. minimum number of arrows to burst balloons (medium)
Recurrence of fastjson historical vulnerabilities
Can TSMC Samsung build a production line without American equipment for Huawei?
Recommend a Hongmeng instant messaging software "fruit chat", which is a bit awesome!!
为华为打造无美系设备的产线,台积电三星能做到吗?
[meetup preview] openmldb + ONEFLOW: link feature engineering to model training to accelerate machine learning model development
至少42名员工感染新冠病毒!诺基亚宣布关闭印度电信设备工厂
网络设备硬核技术内幕 防火墙与安全网关篇 (十一) 零接触办公的奥秘 上
Network device hard core technology insider firewall and security gateway (10)
The Canadian court found Meng Wanzhou guilty of "dual criminality", and the extradition procedure will continue!
Data analysis: disassembly method (details)
自动推理的逻辑09–自动定理证明
![[CruiseControl]Build Result JSP](/img/80/11c2b539c217ecd6ba55668d3e71e9.png)








