目录
signed char 和 unsigned char在内存中的取值范围
数据的类型基本归类
数据在内存中的存储类型大概有以下几种:
1、字符类型 char
2、整型 int
3、短整型 short
4、长整型 long
5、更长的整型 longlong
6、单精度浮点型 float
7、双精度浮点型 double
整型家族:
char(在内存中存储的形式是ASCLL码值,本质上是整型)
int
short
long
long long(C99中定义的)
其中也分有符号整型(signed)和无符号整型(unsigned)
对于int、short、long来说,加signed 前缀和没有前缀是一样的,也就是 signed int=int;
但是对于char来说不是这样的,char不加前缀的情况下具体是signed还是unsigned是取决于编译器的。
在定义一些没有负数概念的变量(如:身高体重)时,我们可以用unsigned,这样可以使数据较之signed多一位计数位。(unsigned没有符号位)
浮点型家族:
float(单精度,精度相对较低,存储的数据范围相对较小)
double(双精度,精度相对较高,存储的数据范围相对较大)
除了上面的基本类型,C语言中还规定了一些其他类型,如构造类型:
1、数组类型
2、结构体类型(struct)
3、枚举类型(enum)
4、联合类型(union)
其他的还有指针类型:
void * int * char * double* .........
特殊的,还有空类型:
void表示空类型,通常表示函数的返回类型、参数、指针类型。
整型在内存中的存储
知道了基本的存储类型,那么数据在内存中又是怎么存储的呢?
先来看整型的存储方式。
在说明前先介绍原码、反码、补码。
原码、反码、补码
原码就是数据打印出来时的时候直接通过正负的形式写出的二进制序列
反码就是原码符号位不变,其他位按位取反所得到的
补码就是反码+1
正整数的原反补码是相同的。
负数则需要通过运算获得。
举个例子:
int a= -5;
它在内存中是怎么存储的呢?
首先,-5的原码是10000000000000000000000000000101
反码是111111111111111111111111111111111010
补码是111111111111111111111111111111111011
将其转化成十六进制显示(编译器显示内存存储的方式,便于观察)
a= -5 : ff ff ff fb(4个二进制位转化成1个十六进制位)
我们来看一下 -5 在VS2022底下具体是怎么存储的:
我们发现它存储的顺序和我们的是反的,这就涉及到大小端了。
大小端介绍
数据在内存中存储的时候,会涉及到大小端字节序的问题。
大小端也就是数据存入以及取出的方式。
大端存储:将数据的低位字节序的内容放在高地址处,将高位字节序的内容 放在低地址处。
如:
大端存储就是将高位字节序(ff)放到低地址处,将低位字节序(fb)放到高地址处。
小端存储正好相反, 小端存储就是将高位字节序(ff)放到高地址处,将低位字节序(fb)放到低地址处。
总而言之,数据是如何放入内存的,就要怎么拿出来。
signed char 和 unsigned char在内存中的取值范围
先来看unsigned char,char在内存中只占一个字节(8个bit位),所以取值从00000000 ~ 11111111
因为unsigned没有符号位,所以8个位都计数,取值范围0~255
signed char:
00000000~01111111与unsigned char 是一样的。
但是符号位为1时变成负数,取值从-1~-128,所以signed char取值范围:-128~127
浮点型在内存中的存储
先看个例子:
是不是与你预想的不太一样。怎么这里的Floatn和 n 就变了?
这是因为浮点数的存储方式和整型不一样,所以以浮点数的视角观察整型存储和用整型视角观察浮点数存储得到的结果是不同的。
下面具体介绍一下浮点数的存储方式。
这里就涉及IEEE754标准定义了:
什么意思呢?浮点数可以表示为以下形式:
(-1)^S * M * 2^ E
(-1)^S 表示符号位,S=0时为正,S=-1时为负。
M 表示有效数字,M>=1且M<2
2^E 表示指数位
这也就是科学计数法。举个例子:
浮点数 3.5 按上述方式表示:
先把3.5以二进制形式表示:11.1( 2^1+2^0+2^(-1) )
此时,S=0,M=1.11,E=1 =(-1)^0 * 1.11 * 2^1
如果是3.14这种数字,转化为二进制时,小数点后无法完全精确得出0.14,只能无限接近,这就是为什么浮点数总是缺省精度的原因,而double比float精度高的原因就是它是64位,比float32位多了一倍,能有更多位存储,去逼近这个小数值。
可以看到,如果数据是以float类型存储的,内存中使用一个字节去保存S,8个字节保存E,剩余的23个字节保存M,并且这里的M因为必定>1,所以省去1,保留后面的位,最后取出时加上1就行了,这样就多一位存储数据,提高精度。而E在存储时,是按它的有效值+127进行存储的,比如E为-1,+127就是126,按126存储。
同样的,double里面也是一样的运作,只是比float多了32位,精度更高。同时这里E按照E+1023存储。
存储是这样的,取出时有三种情况:
1、E不为全0或全1:
S就是第一位的值,M加1,再补上小数点,float的话 E-127,double E-1023
比如内存中存的是 0 10000001 11000000000000000000000
S=0, E=100000001 (127+1-127=1) M=11000000000000000000000 (1.11)
最后还原出来就是:(-1)^0 * 1.11 * 2^1
2、E全0:
此时还原E直接为-126,M不加1,写成0.00xxxxxxx的形式
因为E全0此时的值太小了,无限接近于0
3、E全1:
此时与E全0情况相反,值过大,接近于无穷,正负值取决于S。