整型和浮点型数据的储存

1.基本数据类型

char             //字符型数据类型

short            //短整型

int                //整型

long             //长整型

long long     //更长的整型(C99)

float             //单精度浮点数

double         //双精度浮点数

类型的意义:

1.决定了开辟空间的大小

2.决定了看待内存空间的视角

例:int类型和float类型同样都是向内存申请4个字节的空间,从int的角度认为这四个字节的空间放的是整型,而从float的角度认为这四个字节的空间存放的是浮点数。(具体怎么看待后面会讲)

类型的基本归类:

整型类型

char                                   //字符的本质是ASCII码值,是整型

       unsigend char            //unsigend为无符号

        sigend char               //sigend为有符号

short               

        unsigend short [int]   

        sigend short [int]       

int

        unsigend int              

        sigend int                  

long 

        unsigend long [int]

        sigend long [int]

浮点数类型

float

double

构造类型(自定义类型):

数组类型               //int arr[10]的类型为去掉数组名arr之后保留的int [10]

结构体类型 struct

枚举类型 enum

联合类型 union

指针类型

int *pi

char *pc

float* pf

void* pv

2.整型在内存中的存储

数值有多种不同的表现形式,如二进制,八进制,十进制,十六进制等。而在内存中,数值的储存是以二进制的形式储存的。

1.原码、反码、补码

计算机中整数有三种2进制表示方式,即原码、反码、补码。

三种表示方式均有符号位(最高位为“0”则为正,为“1”则为负)数值位两部分,且正数原码、反码、补码均相同。

负数的原码、反码、补码则需要我们去记忆

原码

直接将数值按照正负数的形式翻译成二进制即可得到原码

反码

符号位不改变,数值位取反(若为1则化为0,若为0则化为1)得到反码

补码

反码+1得到补码

值得一提的是,负数的原码和补码互为原补码。将原码取反+1得到补码,而将该补码取反+1又能得到原来的原码。

2.整型数据在内存中的储存方式是以补码进行的

为什么呢?

CPU只有加法器,计算1-1时是按照1+(-1)的形式进行的而非1-1

32位环境下,int类型的

 1的补码为00000000000000000000000000000001(32位)

-1的原码为10000000000000000000000000000001(32位)

-1的反码为11111111111111111111111111111110(32位)

-1的补码为11111111111111111111111111111111(32位)

将二者的补码相加为100000000000000000000000000000000(33位)

因为其超出了32位,所以第33位的1丢失,二者相加的结果为

00000000000000000000000000000000(32位)对应的结果为0

其他正数负数相加的结果同理

而且原码补码的转换过程是相同的,不需要增加额外的硬件电路。

3.大小端

以VS2022为例,

a=10的二进制应为00000000000000000000000000001010,鉴于2进制的32位数字太长,我们将其转化为16进制,应为0x00 00 00 0a(0x指的是用16进制表示),再来看看VS2022上内存的结果注:该结果显示的虽然是16进制,这是因为我将列数修改为4列,而不是储存结果为16进制,其在内存中的储存结果仍为2进制

它的结果竟然是0a 00 00 00,和预期结果是相反的,这究竟是怎么一回事呢?

这里我们以0x11 22 33 44作为例子来说明(11,22,33,44各占一个字节),以字节为单位讨论内存的存放方式。

他在内存中的存储方式有两种:

1.低地址 11 22 33 44 高地址(大端字节序存储)

2.低地址 44 33 22 11 低地址(小端字节序存储)

11 22 33 44从低字节到高字节分别是44 33 22 11(理解不了可以想一下一个三位数,百位是不是比十位更高,十位是不是比个位更高,这个也是如此)

若我们把11(高位字节)放在低地址处,把44(低位字节)放在高地址处,这样的存储方式称之为大端字节序存储

若我们把44(低位字节)放在低地址处,把11(高位字节)放在高地址处,这样的存储方式称之为小端字节序存储

总结:

大端储存模式

指的是把高位数据放在低地址处,把低位数据放在高地址处。

小段储存模式

指的是把低位数据放在低地址处,把高位数据放在高地址处。

设计一串代码区分当前的字节序

#include<stdio.h>
int main()
{
	int a = 1;//00 00 00 01,计算机读取数据是从低地址向高地址进行读取的
	//小端字节序第一个字节为01,大端字节序第一个地址为00
	if (*(char*)&a)//将a的地址强制转换成char*类型,并进行解引用
		//转化为char*的原因是int*访问的是地址后四个字节的数据
		//而char*访问的是地址第一个字节的数据
		printf("小端\n");//若为01则为小端字节序
	else
		printf("大端\n");//若为00则为大端字节序
	return 0;
}

来看看结果

4.整型数据的打印

先来看看用%d打印数据的结果

#include<stdio.h>
int main()
{
	char a = -1;//char在不同编译器中可能默认有符号,也可能默认无符号
	//我们这里看看VS2022中char默认有无符号
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d\tb=%d\tc=%d\n", a, b, c);
	return 0;
}

来看结果

为什么会出现这样的结果呢?

-1的原码是10000000000000000000000000000001

-1的反码是11111111111111111111111111111110

-1的补码是11111111111111111111111111111111

a是char类型的数据只能存放8个二进制位,发生了截断(只取最后的8个二进制位)

所以a就为11111111,%d是打印有符号的整型,所以发生了整型提升,对于有符号的数来说,整型提升的规则是高位补符号位

变成打印11111111111111111111111111111111(这依然是补码,所以取反+1)

即打印10000000000000000000000000000001即为-1

b同理

unsigned char c是指一个无符号的字符型数据

同上,c存放的补码为11111111,但是c是一个无符号的字符型数据,最高位不为符号位。发生整型提升时,无符号的数据在高位上直接补0。

所以我们用%d打印的数为00000000000000000000000011111111,这还是补码。又因为用%d打印的是有符号的整型,最高位为0,表示它是整数,原反补相同,打印结果为255。 

再看看用%u打印

#include<stdio.h>
int main()
{
	char a = -128;//char类型可存储-128-127
	//原码10000000000000000000000010000000
	//反码11111111111111111111111101111111
	//补码11111111111111111111111110000000
	//发生截断a - 100000000
	printf("%u\n", a);
	//%u - 打印无符号的整型
	//发生整型提升,a最高位为1,高位全补1
	//变成11111111111111111111111110000000
	//因为打印的是无符号的数,所以直接将它打印
	
	return 0;
}

运行结果

若char a=128(a存不下128)再用%u去打印,结果仍为这个值,大家可以自己思考一下为什么会出现这个结果

再附一张整型提升的图片

从下往上提升

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

1.整型的储存方式和浮点数的储存方式不一样

#include<stdio.h>
int main()
{
	int n = 9;
	float* pf = (float*)&n;
	printf("n的值为%d\n", n);//以int的视角打印n
	printf("*pf的值为%f\n", *pf);//以float*的视角打印&n
	*pf = 9.0;
	printf("n的值为%d\n", n);//以int的视角打印n
	printf("*pf的值为%f\n", *pf);//以float*的视角打印&n
	return 0;
}

只是把整型改变成了浮点数,打印的结果就完全不一样。从中不难发现.整型的储存方式和浮点数的储存方式是不一样的

2.浮点数的储存

浮点数存的过程

IEEE 754 标准 一.IEEE 754基本存储规则 (-1) ^ S * M * 2 ^ E 其中S用来控制正负,当S为0浮点数表示为正,反之为负。 M则代表大于等于1且小于2的一个有效数字。 2^E表示指数位。

例:

V = 5.0f;-->二进制形式为101.0   --> 1.01 * 2^2

   =1.01 * 2^2

   =(-1)^0 * 1.01 * 2^2

        S=0  M=1.01  E=2

IEEE 754 规定:

对于32位的浮点数(float),最高的1位储存符号位S,接着的8位储存指数E,剩下的23位储存有效数字M

对于64位的浮点数(double),最高的1位储存符号位S,接着的11位储存指数E,剩下的52位储存有效数字M

IEEE 754 对有效数字M和指数E的特别规定

因为1 <= M < 2所以M可以写成1.XXXXX的形式,其中XXXXX表示小数部分
IEEE 754 规定,保存M的时候,数的第一位总是1,所以将其省略,进而增加浮点数的精确值。
例:32位浮点数,保存1.01时,只保存01部分,在读取的时候再将1加上去,这样能保存24位有效数字

E为一个无符号正数(unsigned int)

如果E为8位,取值范围为0-255;如果E为11位,取值范围为0-2047。但是科学计数法中E是可以出现负数的,为此IEEE 754 规定,存入内存时,E的值需要增加一个中间数,8位的E的中间数为12711位的E的中间数为1023。比如,一个E为10的数,在保存成32位浮点数时,必须保存成10+127=137,即10001001

浮点数取的过程

指数E从内存中取出有3种情况:

E不全为0或不全为1

这时,指数E的计算值减去中间数,得到真实值,再将有效数字M前加上第一位的1

比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移一位,则为1.0*2^(-1),指数E为-1+127=126,表示为01111110,而尾数1.0去掉正数部分为0,补齐0到23位

0  01111110  000000000000000000000000

E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原成0.XXXX的小数。这样做是为了表示+-0,以及接近于0的很小的数字

0  00000000  001000000000000000000000

E全为1

这时,有效数字M全为0,表示正负无穷大

0  00000000  000000000000000000000000

  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值