C语言中的数据存储

1.数据类型

c语言自带的一些数据类型

char      //字符型       1byte
short     //短整型       2byte
int       //整型         4byte
long      //长整型       4 or 8byte
long long //更长的整型    8byte
float     //单精度浮点数  4byte
double    //双精度浮点数  8byte

long在不同环境中字节大小不同(比如VS中是4byte)

1.1类型的分类

整型

char
	unsigned char
	signed char
short
	unsigned short
	signed short
int
	unsigned int
	signed int
long
	unsigned long
	signed long
long long
	unsigned long long
	signed long long

字符在内存中存放的是ASCII码值,所以也算作整型
在写代码的时候,不说明类型是否有符号,默认为signed,char类型除外,它的默认取决于编译器
如果在定义时,定义的为signed 整型,那么其在内存中存储的n位二进制的最高位为符号位,符号位为1表示负数,符号位为0表示正数,对于无符号数,最高位也是计数位,所以计数范围更大
浮点型

float
double
long double

构造类型(自定义类型)

数组类型
结构体类型 struct
枚举类型  enum
联合类型  union

指针类型

int* p1
void* p2
float* p3

空类型

void

常见于函数传参,函数返回值类型,指针类型

2.整型在内存中的存储

整型的取值范围在头文件<limits.h>中定义

整数的二进制表示形式有3种:
原码:整数的二进制形式(多少位由类型所占多少字节空间决定)
反码:原码符号位不变,其他位按位取反
补码:反码加一
正整数的原码,反码,补码相同,都等于原码
负整数的三种形式才需要按上述方式计算

int a = -5
//原码:10000000 00000000 00000000 00000101
	0x80 00 00 05
//反码:11111111 11111111 11111111 11111010
	oxff ff ff fa
//补码:11111111 11111111 11111111 11111011
	0xff ff ff fb
要注意符号位不变

内存中存放整数时存放的其二进制补码
事实上,原码和补码互换的运算逻辑是一致的,补码等于原码取反加一,而原码也等于反码取反加一(可见如今的计算机能有今天,要历经多少天才的发明)

2.2大小端

我们在编译器中看内存窗口时,会看到数据在内存按地址的存放顺序,那数据在内存中存放的逻辑是怎样的呢?
有下面两种存放方式
字节序:字节数据在内存中的存放顺序
大端字节序存储:指将高位字节存储在内存的低地址,而低位字节存储在高地址
小端字节序存储:指将低位字节存储在内存的低地址,而高位字节存储在高地址

2.3整型提升

整型在存放和计算过程中还会涉及到整型提升和截断,可以看我另一篇博客

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

浮点数的表示范围在头文件<float.h>中定义

如果想明白这个问题,我们必须清楚小数的概念

3.1二进制小数

所有的实数按照能否用分数表示分为两大类
1.有理数(整数,分数)
2.无理数(不能表示成任何分数形式的数)
而小数,是实数的一种位值制的书写形式,而不同进制决定了这种书写的具体规则,本质上是用整数部分+小数部分的统一格式表示
你可能会问,说这么复杂,和标题有什么关系?
我们来分析一下小数的书写规则
在十进制中(基数为10)
小数点的左边分别为100(个位) ,101(十位),102(百位)....小数点右边为10-1(十分位),10-2(百分位),10-3(千分位)....
小数点每左移一位,数值10-1
在二进制中(基数为2)
小数点的左边分别为20(个位),21(二位),22(四位)...,小数点右边为2-1(1/2),2-2(1/4),2-3(1/8)....
小数点每左移一位,数值
2-1
在16进制中(基数为16)
小数点的左边分别为160(个位),161(16位),162(256位)...,小数点右边为16-1(1/16),16-2(1/256),16-3(1/4096)....
小数点每左移一位,数值*16-1
。。。。。。
任何一种进制都有其自己的整数部分和小数部分的表示形式,无论是有理数还是无理数,而分数就是分数,不受进制的影响

想必你现在已经明白小数的定义了,还理解了二进制小数部分的运算规则

3.2不同进制间的小数表示

我们知道,不同进制之间的整数部分可以相互转换,小数部分也是可以的
但对于小数部分存在着转换的限制
比如十进制小数转化为二进制
一个分数能写成有限小数,在不同进制中条件是不同的
在十进制,分数能写成有限小数,只有一个条件,把分母化成最简形式后,分母的质因数(既是质数,又是某数的因数)只能是2或5
1/2 = 0.5
1/5 = 0.2
1/10 = 0.1
但1/3,1/7不能化为十进制的有限小数
在二进制中,分数能写成有限小数,最简分母的质因数只有2
1/2 = 0.1
1/4 = 0.01
1/8 = 0.001
你会发现1/10(质因数包含2和5),在二进制中无法精准表示,这也解释了为什么有些十进制小数无法用二进制来精确表示(0.1就不行)这是因为二进制小数的分母的质因数只有2,没有5,所以他不能表示十进制中最简分母质因数包含2和5的小数
其他进制也是同理,当然不同进制间的小数转换,这是个复杂的问题,甚至包含有限小数转无限小数,这里只探讨十进制和二进制,这也是主题要用到的

3.3二进制小数的别样表示

我们给出一个十进制浮点数10.5
转换为二进制是1010.1
它可以写成1.0101*23(类似于科学计数法就比如123.456可以写成1.23456 *102
还可以写成(-1)0 *1.0101 *23
我们发现,对于任意的一个二进制浮点数(不考虑不能写成二进制小数的十进制小数)都可以写成
v=(-1)^S *M *2E
这里v代表浮点数,S代表符号位,M代表有效数字,E代表指数位
当S为1时表示负数,S为0时表示正数
E控制小数点的位置
M是存储的有效部分,整数部分不存储,读取时默认为1,只存小数部分(二进制使用这种计数法,整数位只可能是1)
既然任何一种二进制浮点数都可以表示成这种形式,那么浮点数在内存中的存储只需存储3个变量S,E,M

3.4浮点数的存储

对于32位的浮点数IEEE规定
S占1bit(最高位)
E占8bit(中间)
M占23bit(最后)
default

对于64位的浮点数来说
S占1bit
E占11bit
M占52bit

对于M,IEEE754规定,在计算机保存M时,默认这个数的第一位总是1,因此舍去,只保留后面的小数部分,当读取的时候,再把1加上
对于E,首先其存储时是unsigned int,但实际情况中E有可能是负数,所以IEEE754规定,在存储E的时候要加上一个偏移量,把计算的结果存进去,单精度的是127,双精度的是1023
比如我要存-1进去,实际存放的E是126
存储的位数不够32位或64,在后面补0
5.5f
101.1
(-1)0 *1.011 *22
s = 0,M =1.011 e=2 (129)
32位 0 10000001 011 00000000000000000000
0x40 b0 00 00(显示成16进制)
这就是5.5在float类型变量中的存储

3.5浮点数取出

当E不为全0或者不全为1

把e存储的值减去偏移量,再把m拿出来最前面补上1,再按上面的计算公式进行计算

当E为全0

把e直接看作1-127或1-1023,m不再补1,而是补0看作0.xxxxx的小数
这样做是为了表示接近于0的小数和正负0

当E为全1

如果m全为0,表示正负无穷大

posted @ 2025-11-10 18:22  好想成为人类啊  阅读(19)  评论(0)    收藏  举报