1.整数在内存中的存储

我们知道数据在内存中都是以2进制的形式存储的;比如int,char,double,float这些类型的数据都是以2进制的形式去存储的,那么这些数据又是如何去存入/取出的呢?

前面我们知道,整数分为有符号整数和无符号整数;而整数在内存中存储有三种表示方式,分别是原码,反码和补码;三种表示方式均分为符号位和数值位

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。

对于有符号的整数来说原码,反码和补码是要进行计算的,而对于无符号的整数来说原码,反码和补码来说都相同;下面我们来一一介绍:

有符号整数

对于有符号整数来说,分为正数和负数;

正数的原码,反码,补码相同;

负数的原码,反码和补码要进行计算;

举例说明:

对于有符号整数来说最高位是符号位,0表示正数,1表示负数;后面31位表示数值位也就是有效位;

无符号整数

无符号整数的32个比特位都表示有效位;

举例说明:

2.大小端字节序

我们在观察内存的时候,不难发现我们观察到的数据储存是倒着存的;

比如:

我们发现是先存44 33 22 11;这种倒着存放数据的也就是我们的小端字节序存储;

什么是大端,什么又是小端呢?

大端存储:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。

小端存储:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存
在内存的⾼地址处。

这个不难理解,

练习1

判断当前机器上大端还是小端存储;

可以用指针来,也可以用联合体来表示,联合体的方法可以看前面的联合体介绍;

int main(){int a = 1;char* pa = (char*)& a;if (*pa){printf("小端\n");}else{printf("大端\n");}return 0;}

练习2

#include int main(){ char a= -1; signed char b=-1; unsigned char c=-1; printf("a=%d,b=%d,c=%d",a,b,c); return 0;}

题目解析:

这里出现了一个词,整形提升;

如果我们要打印一个将一个char或者short以int类型去打印的话,我们是不足32个比特位的(char 8bit)(short 16bit),那么我们就要补齐剩下的位,那么这里也分2种情况;

如果是无符号整形的话,直接补0即可;

如果是有符号整形,按自己的符号位往上补;也就是我们上面的那种情况;

接下来我们继续:

练习3

#include int main(){ char a = -128; printf("%u\n",a); return 0;}

题目解析:

练习4

#include int main(){ char a = 128; printf("%u\n",a); return 0;}

题目解析:

我们发现不管是有符号的char,还是无符号的char这貌似是一个循环;

所以这一题可以显而易见的看出我们存进去的依旧是-128;

那么打印的结果就是跟上一题一样;

练习5

#include int main(){ char a[1000]; int i; for(i=0; i<1000; i++) { a[i] = -1-i; } printf("%d",strlen(a)); return 0;}

题目解析:

练习6

#include unsigned char i = 0;int main(){ for(i = 0;i<=255;i++) { printf("hello world\n"); } return 0;}

题目解析:

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

首先我们先来看一段代码:

#include int main(){ int n = 9; float *pFloat = (float *)&n; printf("n的值为:%d\n",n); printf("*pFloat的值为:%f\n",*pFloat); *pFloat = 9.0; printf("num的值为:%d\n",n); printf("*pFloat的值为:%f\n",*pFloat); return 0;}

对于这个答案我们很疑惑;

这就涉及了浮点数在内存中的存放和取出的规则了,下面一起来了解一下浮点数的存放和取出的方式;

根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^s∗M ∗ 2^E
• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数

• M 表⽰有效数字,M是⼤于等于1,⼩于2的

• 2^E 表⽰指数位

如果我们要把它写成上面那种形式的话就是:


IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M(float)
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M(double)


1.浮点数存的过程

前面我们知道,M是大于等于1,小于2的,通常写成1.xxxxxxxx,所以再存浮点数的有效数字M的时候不会去存小数点前面的1,只会存小数点后面的数,等取出来的时候再放个1放前面;

对于指数E来说,它是一个无符号的整数,但是指数E是可能为负数的,比如0.5,此时指数E为-1;所以IEEE 754规定,在存入E的时候加上一个中间数,如果是4位浮点数的话就加上127,如果是8位浮点数的话就加上1023;。⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

我们举例说明:

2.浮点数取的过程

S和M取出的方式比较简单,主要E情况特殊,有三种情况:

1.E不为全0或不为全1

这种情况是属于正常的情况,取出来的时候直接减去127(1023)即可;

⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位00000000000000000000000,则其⼆进制表⽰形式为:

0 01111110 00000000000000000000000

2.E为全0

这种情况比较特殊,我们加上了127(1023)最后E还是为全0;那只有可能E为-127(-1023),说明这是一个无限接近于0的数字;此时就不能直接减去中间数了,这个时候1-127(1-1023)即为真实值;有效数字也不在加上一,而是加上0;这样做是为了表⽰±0,以及接近于0的很⼩的数字;

3.E为全1

这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);

0 11111111 00010000000000000000000

题目解析:

下面我们来看一下最开始的那一道题目;