目录
整形:
大小端存储
浮点数的存储
整形提升和算数转换:
编程中,各类计算机语言都对外提供了很多数据类型,那么在程序运行的过程中,数据在内存中是嗯么去进行存储的?
编程中的数据类型,以c语言为例,
整形分四种,细分又分有符号和无符号两种类型
有符号:存储的二进制最高位最为符号位(0代表正数,1代表负数);
无符号:存储得时候不存在符号位;
//char在底层存储的时候存储的也是整形,在使用的时候会参照ANSII码值;c语言中占一个字节
char a = 1;
signed char
unsigned char;
//短整型,占2个字节
short a1 = 1;
signed short
unsigned short
//整形,占4个字节
int a2 = 1;
signed int
unsigned int
//长整形,占4个字节
long a3 = 1;
signed long
unsigned long
在对 a 进行赋值操作的时候,
1 是默认 int 整形,他的二进制数为 0000 0000 0000 0000 0000 0000 0000 0001
因为 a 是char类型的,在存储得时候只占一个字节,就是8个比特位,就会对这个 1 进行截取,所以这个 1 在存到 a 中的时候就变成了 0000 0001
相同的 1 在short中的存储就是 0000 0000 0000 0001
在存到 int 中的时候,就直接放进去
一样的道理,long在内存中也占4个字节。
整形在存储的时候,存储的都是这个数据的补码;说到这个补码,那就得说说原码,反码和补码之间的转换了:
//一个数的原码就是根据十进制和二进制的转换规则转换过来,
//正数的反码和补吗和原码一样,
//负数的反码就是其原码的符号位不变,将其他位逐一取反所得,
//负数的补吗就是将其反码加1;
//以整数10和-10举例说明
//正整数 10
//原码 0000 0000 0000 0000 0000 0000 0000 1010
//反码 0000 0000 0000 0000 0000 0000 0000 1010
//补吗 0000 0000 0000 0000 0000 0000 0000 1010
//负整数-10
//原码 1000 0000 0000 0000 0000 0000 0000 1010
//反码 1111 1111 1111 1111 1111 1111 1111 0101
//补码 1111 1111 1111 1111 1111 1111 1111 0110
负整数的原码转换到补码:除符号位之外,取反 + 1即可,
从补码到原码:可以原路返回,-1 取反符号位之外的,另外还有一种方法,那就是 取反 + 1,就是从原码到补码的方式,同样也可以达到同样的效果。
以上就是有符号整形在内存中的存储了,
那么无符号整形,就好理解了,在存储的时候,全部的比特位用来存储数据,没有符号位,
举例
//因为无符号,所以原反补都一样
signed int a1 = 10
//存储的补码:0000 0000 0000 0000 0000 0000 0000 1010
unsigned int a2 = 10;
//存储的补码:0000 0000 0000 0000 0000 0000 0000 1010
//但是如果这样操作:
signed int a3 = -10;
//存储的补码:1111 1111 1111 1111 1111 1111 1111 0110
printf("%u\n",a3);//以无符号整形进行输出
//那么编译器会直接将a3在内存中的存储当成一个无符号整形,那也就没有符号位,将会是一个非常大的数字
//转换十六进制:输出结果就是0xff ff ff f6
另外一个点,那就是计算机的大小端存储,程序在运行过程中,数据在内存中的存储也分为大端字节序存储和小段字节序存储,这个和编译器没关系,是计算机本身对数据存储的一种决定方式。
大端字节序存储:指的就是数据的高位存储在内存低地址端,例如
//对于一个十六进制数 0x12345678,"12"是这个数据的高位,
//那假如说左边就是内存中的低地址的话,那么这个十六进制数在内存中的存储就是 0x12 34 56 78
小端字节序存储:指的就是数据的低位在内存中的低地址处
//对于一个十六进制数 0x12345678,"78"是这个数据的低位,
//那假如说左边就是内存中的低地址的话,那么这个十六进制数在内存中的存储就是 0x78 56 34 12
好了,说完整形在内存中的存储,就是浮点数在内存中的存储了
首先,要区分一下,整形是有分有符号数,和无符号数的,但是浮点数没有有符号和有符号之分。
接下来我们详细剖析:
//浮点数分为 float和double,
//其中float在存储得时候占4个字节32个比特位,double占8个字节64个比特位。
float a = 10.5f;
//这样一条语句,在底层存储的时候,要先将其转化为科学记数法将其表示
// 10.5, 其中,整数部分呢,比较好表示,10表示为十六进制就是 1010,那么这个0.5是怎么表示的。
//从二进制的权值表示不难推断:
// 0 0 0 0 . 0 0 0 0
// 2^3 2^2 2^1 2^0 2^-1 2^-2 2^-3 2^-4
//所以小数点后面的数字就需要一点一点去凑,比如0.5 就是0.1, 0.75就是0.11
//那我们就知道了10.5,用二进制表示出来就是 1010.1
//好了,现在把他换成科学记数法用来表示:1.0101*2^3
//但其实浮点数也有正负之分吗,那还需要进一步表示成:(-1)^0 * 1.0101 * 2^3
//好了到这里,我们就可以总结一下他的表达式:(-1)^S * M * 2^E
//对于S,他只需要0或者1,就可以完成对正负的表示,
//对于M,将所有的数字转换为这样子存储之后,M的第一位永远都是1,所以呢在进行存储的时候,可以对其进// 行省略,后序要对其进行操作的时候,在补上,这样就可以多出一位来存储数据了,
//对于E,他是一个无符号的整数,但是实际情况下,他也有负数的情况,为了应对这种情况呢,就要对其进行
// 矫正,如何矫正呢?在float类型32位字节存储的时候,会给E留出8个二进制位;在double类型64位字// 节存储的时候呢,会为其留出11位,所以规定,在进行E的存储的时候,会为其加上一个中间数,来让这// 个E扮演有符号的角色,float会为其加上中间数127,double则会为其加上中间数1023,用以矫正。
//用float进行存储的话,这32位怎么划分
float a = 10.5f;
// 0 0000 0000 0000 0000 0000 0000 0000 000
// S E M
//那么对10.5进行存储,S = 0; M = 3 + 127 = 0x1000 0010; M = 0101
// 0 1000 0010 0101 0000 0000 0000 0000 000
// 0100 0001 0010 1000 0000 0000 0000 0000
//换算十六进制:0x41 2f 00 00 如果是小端字节序的话那就是 0x00 00 2f 41
//用double存储的话,64位怎么划分
double b = 10.5;
//0 0000 0000 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
//S E M
//那么10.5进行存储:S = 0; M = 3 + 1023 = 0x1000 0000 010; M = 0101
//0 1000 0000 010 0101 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
// 0100 0000 0010 0101 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
//换算十六进制:0x41 25 00 00 00 00 00 00,小端存储就是0x00 00 00 00 00 00 25 41
在从语言中,整型算术运算总是至少以缺省整型类型的精度来进行的,那就是int。当两个小于int整形的的操作数在计算机中进行计算时,计算机为了获得精度会先将两个类型在计算前先转换为int的整形,这个就叫做整形提升。
先看一个例子
#include
int main(){
char c = -10;
printf("%u\n",c);
return 0;
}
内存中存储的是-10,但是以无符号整形对其进行打印的时候,他还会是-10吗?
并不是,打印的结果是4294967286。这是为什么呢,咱们来分析一下
首先 -10放到char类型中,他存放的肯定是补码:咱们算一下
原码:1000 1010
反码:1111 0101
补码:1111 0110
那么在进行无符号打印的时候,这个-10就需要进行整形提升了,首先,在提升的时候,是不会有无符号这个概念的,所以要在其高位进行补齐,算数补齐补的是符号位,补完之后是这样的
1111 1111 1111 1111 1111 1111 1111 0110
现在呢在对其进行无符号整形打印,那就会视这段补码为无符号整形进行打印,那就不存在符号位,就直接当做正数进行输出,而这段二进制用计算机计算得到,他的十进制数就是4294967286。
当两个进度小于int的两个数进行算术运算的时候,就会进行整形提升,如果大于int精度的数进行算数运算的话,就要进行算数转换,意思就是先将精度低一些的数据转化为精度更高的那个数据的类型,然后进行运算,根据需要,在进行截断存储,那么精度高低是怎么划分的,
由高到底的顺序,精度也由高到低
long double
double
float
unsigned long
signed long
unsigned int
signed int
unsigned short
signed short
unsigned char
signed char
好了,今天的分享就到这里了