004 数据在内存中的存储形式——“C”
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、类型归类
整形家族
char 1个字节 8bit
int 4个字节 32bit
short 2个字节 16bit
long 4个字节 32bit
char
unsigned char
signed char
short
unsigned short ·
signed short
int
unsigned int
signed int
long
unsigned long
signed long
unsigned 表示只能是整数即无符号整数
signed 表示正负皆可以
自定义类型:
数组类型 结构体类型 struct 枚举类型 enum 联合类型union
指针类型
int* char* float* void*空类型
void表示空类型
通常应用于函数的返回类型,函数的参数,指针类型
二、原码反码补码是什么
原码反码补码是二进制三种表现形式
最高位表示符号位 其余是数值位
原码用于打印数值,计算
补码存储在计算机内
符号位用0表示正,用1表示负
as:-5 5
10000000 00000000 00000000 00000101 -2^1+2^0=-5
00000000 00000000 00000000 00000101 2^1+2^0=5
原码反码补码的转换
原码——>补码
原码10000000 00000000 00000000 00000101
反码111111111 111111111 111111111 111111010 符号位不变,按位取反
补码111111111 111111111 111111111 111111011 +1
第一种方法:补码——>原码 -1取反
111111111 111111111 111111111 111111011
111111111 111111111 111111111 111111010 -1
10000000 00000000 00000000 00000101 取反
第二种方法:补码——>原码 取反+1
111111111 111111111 111111111 111111011
10000000 00000000 00000000 00000100 取反
10000000 00000000 00000000 00000101 +1
在此基础上我们可以扩展一下关于char类型的取值范围
char =1byte字节=8bit
signed有符号的char的取值范围是-128到127
00000001 2^0 1
00000010 2^0 2
00000011 2^1+2^0 3
00000100 2^2 4
00000101 2^2+2^0 5
....
01111111 127
10000000 -128
10000001 -127
...
11111111补码 11111110减1 10000001按位取反 ——>-1
无符号的char的取值范围是0到255
00000001 1
00000010 2
00000011 3
00000101 4
00000110 5
...
01111111 127
10000000 128 无正负之分
10000001 129
11111111 255
三、大端小端字节序存储
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元 都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short 型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因 此就导致了大端存储模式和小端存储模式。
1.大端字节序存储:把一个数据的低位字节的数据,存放在高地址处,把高位字节的数据存放在低地址处
2.小端字节序存储:把一个数据的低位字节的数据,存放在低地址处,把高位字节的数据存放在高地址处
四.国际标准IEEE
1、二进制浮点数的表示形式:
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E 其中E表示小数点向左移动的位数
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。 M表示有效数字,大于等于1,小于2。 2^E表示指数位。
as:-5
101.0=(-1)^1*1.01*2^2
M=1.01
S=1
E=2
as:5.5
101.1=(-1)^0*1.011*2^2
M=1.001
S=0
E=2
0.5的二进制形式为0.1 0.25的二进制形式为0.01 0.125的二进制形式为0.001依次类推
因此浮点数在内存中的存储不一定精确
IEEE 754对有效数字M和指数E,还有一些特别规定。 前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。 IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的时 候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位 浮点数为例,留给M只有23位, 将第一位的1舍去以后,等于可以保存24位有效数字。
2.把指数从内存中取出的三种形式
(-1)^S*M*2^E
1.E全为0
放在内存内竟然是全0,这可是+127 那之前的数字可是+- 1.xxxx*2^-127 一个无限接近于0的数字
还原的时候: 浮点数的指数E等于1-127(或者1-1023)即为真实值
真实值有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。
2.E全为1
有效数字M全为0,表示正负无穷大(正负取决于符号位)
八个比特位 11111111 ——>E=255还原的时候 真实值至少为255-127
3.E不全为0也不为全1
浮点数采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值 再将有效数字M前加上第一位的1
int main()
{
float f = 5.5f;
//101.1
//(-1)^0 * 1.011 * 2^2
//0 10000001(129-127=2(真实值)) 011000000000000000000000(把1放置在前面,这样做可以保存24位有效数字,更加精确)
//
return 0;
}
int main()
{
int n = 9;
float* pfloat = (float*)&n; //指针的类型,以什么样的权限什么样的视角解读,认为float*指针认为内存中的二进制序列是浮点数二进制序列
printf("n的值为:%d\n", n);
printf("*pfloat的值为:%f\n", *pfloat);
*pfloat = 9.0; //以浮点数的视角,采用浮点型的数字
printf("num的值为:%d\n", n); //1091567616
printf("*pfloat的值为:%f\n", *pfloat);
return 0;
}
第一:n=9 用pfloat的指针接收n的地址,*pfloat解引用取出n的值,n==*pfloat,%d解读与用%f解读为何差距如此之大
第二:赋*pfloat==n一个值9.0,用%d解读与%f解读为何差距如此之大
int main()
{
int n = 9;
// //00000000 00000000 00000000 00001001
// //0 E:00000000 M:000000000000000001001
// //E为全零,真实值不用再在前面加上1了,直接还原成0.xxxxx的小数,实在太小了
// //真实值E=1-127=-126
// //M=0.000000000000000001001
// //(-1)^0*0.0000000000000000000001001*2^-126
// //以%f打印只能打印小数点后六位
float* pfloat = (float*)&n; //指针的类型,决定以什么样的权限什么样的视角解读,认为float*指针认为内存中的二进制序列是浮点数二进制序列
//(-1)^0* 0. 000000000000000001001*2^-126=0.000000 float类型读取小数点后六位
printf("n的值为:%d\n", n); ——>9
printf("*pfloat的值为:%f\n", *pfloat); ——>0.000000
*pfloat = 9.0; //以浮点数的视角,采用浮点型的数字
// //1001.0
// //1.001*2^3
// //(-1)^0*1.001*2^3
// //S=0
// //E=3
// //M=1.001
// //0 10000010 00100000000000000000000
// 01000001000100000000000000000000 正数的原反补相同
// 真实值E:130-127=3 补1——> 1.001
printf("num的值为:%d\n", n); //1091567616
// %d最高位为0,原码反码补码相同,
//
printf("*pfloat的值为:%f\n", *pfloat); ——>9.0 真实值
注意这里的(-1)^0*1.001*2^3只是接近于9.0,浮点数在内存中的存储不是一定精确
return 0;
}