【C语言】整数,浮点数数据在内存中的存储

发布于:2024-05-07 ⋅ 阅读:(23) ⋅ 点赞:(0)
Tiny Spark get dazzling some day.

1. 整数在内存中的存储

1.1 原码、反码、补码

我们都知道,数据在二进制中都是以 二进制 的形式存储在计算机中,而二进制只有 01两个数字。

有符号整数的二进制序列中,最高位 称 符号位,表示正负;其他位 称 数值位,表示数值。
无符号整数的二进制序列中,没有符号位,即每一位 均为 数值位
有多少位具体看 该整数的类型大小,比如 int 类型的

整数 的二进制表示 有三种,分别为 原码反码补码(数据存放在内存中的就是 补码 !)。
整数的三种码都是一样的,负数的则有所不同:

  • 原码 是整数数值直接翻译成 二进制 所得。
  • 反码 是将 原码 的符号位不变,其他数值位按位取反所得。
  • 补码反码 + 1 所得。
  • 若从
    举个例子
  • 整数十进制:-1
  • 原码:1 0 0 0 0 0 0 1
  • 反码:1 1 1 1 1 1 1 0
  • 补码:1 1 1 1 1 1 1 1

1.1 大小端存储

先来看个例子

// 我们在编译器中创造一个变量, 看看它在内存中的存储情况
int a = 0x11223344; // 用十六进制数值对 变量a 进行初始化赋值

开启调试 打开内存监视窗口 看一下
在这里插入图片描述
一开始的时候我也有点懵逼,这里不应该是显示为 11 22 33 44 这样吗?
后来我了解到,当 数据大小 >= 2个字节 ,在内存中存储的时候,就会有 字节序 的问题。
存储顺序 就分为 大端存储小端存储

1.2.1 字节序分类

在这里插入图片描述

在这里插入图片描述

  • 大端存储 :是指数据的 低位字节内容 保存在内存的 高地址 处,而数据的 高位字节内容,保存
    在内存的 低地址 处。
  • 小端存储 :是指数据的 低位字节内容 保存在内存的 低地址 处,而数据的 高位字节内容,保存
    在内存的 高地址 处。

简记:
(内容)(顺序)(内容)(顺序)
大端
小端

那么,我们该如何知道当前机器的字节序呢?

1.2.2 判断字节序

  • 第一种
int check()
{
	int i = 1;
	return (*(char*)&i);
}
int main()
{
	int ret = check();
	if(ret == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
	return 0;
}

在这里插入图片描述

  • 第二种
int check()
{
	union // 在联合体中 a 和 b 共用同一块空间
 	{
 		int a;
 		char b;
 	}un;
 	un.i = 1;
 	return un.b;
}
int main()
{
	int ret = check();
	if(ret == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
	return 0;
}

在这里插入图片描述


2. 浮点数在内存中的存储

2.1 浮点数的存储形式

上图获取来自百度百科: IEEE 754:浮点数表示

根据上面的资料,那么任意浮点数 V(Value) 可以表示为
在这里插入图片描述

  • (-1) ^ S 表示表示符号位 正负
  • F 表示 大于等于1小于2 的 有效数字
  • 2 ^ E 表示 指数 位(E 是 unsigned int 无符号整数类型)

举例:
十进制数 9.0 转换称二进制为 1001.0,用科学计数法来表示就是 1.001 x 2 ^ 3
那么, S = 0, F = 1.001,E = 3

单精度的浮点数 和 双精度的浮点数 在内存分配的空间是不同的

  • float 类型的浮点数内存分配

在这里插入图片描述

  • double 类型的浮点数内存分配

在这里插入图片描述

2.2 浮点数的 “ 存 ”

2.2.1 S

首个比特位存 S 表示浮点数的 正负(0 或 1)

2.2.2 E

前面我们了解到,E 是一个无符号整数,在 float类型浮点数中有占 8个比特位,取值范围 0 ~ 255,
double类型浮点数中占 11个比特位,取值范围 0 ~ 2047,也就是说 E 是一个非负整数。
但在科学计数法,是允许存在 负数的 E 存在的。

所以,IEEE 754 制定了规定:

E 存入内存时,其真实值必须加上其取值范围的中间数,float类型的为 127,double类型的则为
1023. 比如,2 ^ 8 的 E 是 8,在存入内存时要 + 127 = 135(假设是单精度浮点数类型),即为二进制 10000111

在这里插入图片描述
资料来自百度百科IEEE 754:指数偏差

偏移值就是 内存值(E存入内存值)真实值(E原值) 的差

2.2.3 F

F大于等于1小于2 的,对于任何浮点数都几乎可以表示 1.xxxxxxxxx
“ 存 ” 的时候,F 只存小数部分的 xxxxxxxxx ;在 “ 取 ” 的时候,再把整数部分的 1 加上去

有啥作用?

(假设是单精度浮点数类型)F在存的时候占23位bit,如果把整数的 1 也存进去的话,小数部分能存22位。 但是如果在 “ 存 ”
的时候,F 只存小数部分的 xxxxxxxxx ;在 “ 取 ” 的时候,再把整数部分的 1 加上去,
那就可以节省1位有效数字,小数部分就可以存23位多一位。

2.3 浮点数的 “ 取 ”

2.3.1 S

S (0 或 1)就正常取,是啥取啥就完了

2.3.2 E、F

  • E 不全为 0 或 不全为 1

在这里插入图片描述

这种情况下只要把 E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E
F 把整数部分的 1 加上小数部分即可

  • E 全为 0

在这里插入图片描述

这种情况表示 浮点数是一个接近0的很小的数 或者就是 0
E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E
F 不再加上整数部分的 1 ,而是还原为 0.xxxxxxxxx 的小数

  • E 全为 1
    在这里插入图片描述
    如果 F 全为 0,则表示这是一个 正无穷大负无穷小 的 浮点数(取决于符号位 S
    E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E

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("n的值为:%d\n",n);
 	printf("*pFloat的值为:%f\n",*pFloat);
 	return 0;
}

输出结果是什么?
在这里插入图片描述

为何有这样的变化?

在这里插入图片描述
我们可以看到 指数 E 部分全为0,说明转化后的小数是一个接近0的很小的数,

0.000000000...后面不全为0,但不为0的数也是在非常后面

而我们使用 printf 函数打印浮点数的时候,再没有限定小数位数的情况下, 默认输出小数点后6位
所以我们看到输出结果的第二行就是

*pFloat的值为:0.000000 // 后面的就不输出了

继续往下,为何第二次打印 n 的值,会变得这么大?
在这里插入图片描述
01000001000100000000000000000000 转化为十进制就为 1091567616

  
  
  Stay hungry. Stay Foolish. 饥渴求知,虚怀若愚。
  感谢各位读者支持,虚心请教,如有错漏或可改进点,请任意指出,感激不尽!
  一起进步!