想知道C语言中数据储存的奥秘吗?那就快来看看这篇博客吧~
1、数据类型介绍
C语言有很多的数据类型:
字符数据类型:char
短整型:short 整形:int 长整型:long 更长的整形:long long
单精度浮点数:float 双精度浮点数:double
我们为什么需要这么多种数据类型呢?因为生活中有很多种具有某种共同特性的数据,比如年龄、分数、工资、存款等等,他们有的一定是正整数,有的可能出现小数,有的肯定在一定小的范围内,有的可能会比较大,有的有正负,有的没有。这时不同的数据类型能让我们更加灵活地来储存使用数据。类型决定了开辟的内存空间的大小,也决定了我们看待内存空间的视角。
1.1类型的基本归类整形家族:
char;(虽然是字符类型,但是存储的时候存的是字符的ASCII值,ASCII是整数)(char不确定是否有符号,取决于编译器) unsigned char;signed char;
short;(默认有符号) unsigned short [int];signed short [int]; //[int]表示int可省略
int;(默认有符号) unsigned int; signed int;
long;(也默认有符号) unsigned long; signed long;
如果是有符号的数据,最高位是符号位。最高位是0表示正数,最高位是1表示负数。
对于无符号数,最高位也是数据位。
浮点型家族:
float; double; long double
构造类型:
数组类型;结构体类型struct;枚举类型enum;联合类型union;
指针类型:
int *pi; char *pc; float *pf; void *pv; //指针变量是用来存放地址的
2、整形在内存中的存储
不仅是整数,数据在内存中都以二进制形式存储,但不同类型存储方式可能不同。而整形存储的是二进制的补码。
2.1原码、反码、补码计算机种整数有三种表示方法:原码、反码、补码。他们均由符号位和数值位组成。正整数的三种表示方法相同,负整数的三种表示方法各不相同。
【原码】:直接将数值按照正负数形式翻译成二进制。
【反码】:将原码的符号位不变,其他各位依次取反就可以得到了。
【补码】:反码+1。
e.g.:int a = 0; //整型值
原码:00000000000000000000000000001010
(正整数反码补码与原码相同,负数的需要计算)
int b = -10;
原码:10000000000000000000000000001010
反码:11111111111111111111111111110101
补码:11111111111111111111111111110110
整数在内存中存储的是补码。
e.g.:1111 1111 1111 1111 1111 1111 1111 0110
f f f f f f f 6
Tip:不同的进制只是数据的表现形式。例如上一行就是十六进制表示。
Q:为什么在内存中以补码形式保存呢?
A:因为CPU种只有加法器,减法乘法等都是转化为加法进行的。如果使用原码进行加法计算则可能得不出正确结论,而补码则可以计算正确。(可以用1+(-1)来验证)
使用补码,可以将符号位和数值域统一处理;同时也统一了加减法。此外,补码和原码相互转换,其运算过程是相通的,不需要额外的硬件电路。
1byte 即 8bit 且有符号(有符号char),则取值范围为:
00000000 00000001 ... 01111111 10000000 10000001 ... 11111110 1111111
-128~127
若为无符号char则为 0~255
2.2大小端介绍大小端——大小端字节序存储。
大端字节序存储:把一个数据的低位字节处的数据放在高地址处,把高位字节处的数据放在低地址处。
小端字节序存储:把一个数据的高位字节处的数据放在高地址处,把低位字节处的数据放在低地址处。
比如:对于11223344来说,11是它最高的位的字节,44是它的最低位字节。
用大端字节数据存储即为:11 22 33 44;用小段字节数据存储则为:44 33 22 11。
Q:为什么会出现大小端?
A:因为对于大于8位的处理器,例如16位或32位的处理器,由于寄存器宽度大于一个字节,那么必然存在一个如何安排多个字节的问题,因此就导致了大小端储存模式。
判断大小端代码:
#includeint check_sys() { int a = 1; return *(char*)&a; } int main() { int ret = check_sys(); if (ret == 1) { printf("小段n"); } else { printf("大端n"); } return 0; }
【关于浮点数的数据存储,未完待续~加油!】
(2022.3.2更新)
3、浮点型在内存中的存储浮点数家族:float、double、long double
(补:limits.h里面有整数的取值
范围相关信息,float.h里面有浮点数的精度和取值范围相关信息)
3.1一个例子#includeint main() { int n = 9; float* pFloat = (float*)&n; printf("n的值为:%dn", n); printf("*pFloat的值为:%fn", *pFloat); *pFloat = 9.0; printf("num的值为:%dn", n); printf("*pFloat的值为:%fn", *pFloat); return 0; }
大家可以先猜猜这个运行结果哦~
真正的运行结果如下:
对于第一个,9放进去按照整数打印得到9是应该的。对于第二个,float类型的指针认为自己访问的是float类型的数,解引用时访问浮点数型的四个字节。然而打印出来并不是9.000000,由此看出,整数的储存和浮点数的储存是有所不同的。对于第三个,我们以浮点数的形式存入一个浮点数9.0,然后用整数的形式打印出来,发现不是9,更能说明浮点数和整数存储方式不同。对于第四个,浮点数的形式放进去,浮点数的形式拿出来,就可以正确打印。
从而得出结论:整数和浮点数在内存中的存储方式是有所差异的!
那么,浮点数在内存中是如何存储的呢?
3.2浮点数存储规则根据国际标准IEEE754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S*M*2^E(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数M表示有效数字,大于等于1,小于22^E表示指数位对于32位的浮点数,最高位的1位是符号位S,接着的8位是指数E,剩下的23位是有效数字M对于64位的浮点数,最高位的1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M
比如:
5.5 ——10进制的浮点数
二进制:101.1
科学计数法:1.011 * 2^2
再加上符号:(-1)^0 * 1.011 * 2^2
S = 0; M = 1.011; E = 2;
M保存时将小数点前的1省略,读取时再加上,可以节省一位有效数字。
E是一个无符号数。而科学计数法时指数可能出现负数,因此,就此规定存入E的真实值时必须再加上一个中间值,对于8位的E来说加127,对于11位的E则是加1023。
如上面的例子5.5:
E = 2 + 127 = 129; M = 011;
存储到内存:0100 0000 1011 0000 0000 0000 0000 0000
0x 4 0 b 0 0 0 0 0
指数E从内存种取出还可以分成3种情况:
1、E不全为0或不全为1:E-127(或1023)得到真实值,再将M前加上1即可
2、E为全0:E=1-127(或1-1023)即为真实值,M也不再加1,而是0.几几几。即为0.几几×2的-126次方,表示很接近0的数字
3、E为全1:表示±无穷大的数字(正负取决于符号位)
(后两者仅作了解~)
了解了这些之后再看一开始的那个代码的例子就可以理解为什么会打印出这样的结果啦!
好啦!关于数据存储的内容到这里就结束啦!谢谢大家~



