序言:
每个学习c语言的人,肯定都知道一个整形(int)占4个字节、一个单精度浮点型占4个字节、一个双精度浮点型(double)占8个字节(取决于编译器)。可他们在内存中是以什么形式存放的,以及浮点型的单、双精度具体是以什么标准来区分的却别有一番学问,看完这篇文章,答案即可了然于心。
整形在内存中的存储:
整形在内存是以二进制的形式存储的,更准确的说,是以反码的形式存储的,下面介绍二进制的三种形式:
原码:
通常情况下由其它进制转为二进制后得到的就是原码。
10000000000000000000000000000011 //-3的原码
反码;
除第一位符号位外(0为正,1为负)将原码每一位的0转为1、1转为0,即按位取反,得到的就是反码。
补码:
将反码加1得到的就是补码。
11111111111111111111111111111100 //-3的反码
原码和补码的相互转换:
原码取反加一得到补码
补码取反加一同样可以得到原码
采取补码存放的优势 :
1,使用补码,可以将数值位和符号位一同处理
1,使用补码,可以将加法和减法一同处理(CUP内只有加法器),可以将 -1转换为+(-1)
3,使用补码,并不会造成额外的性能开销(原、反码的转换方式一样)
小端存储和大端存储:
内存是以字节为单位来存储的,但超过一个字节大小的数据(int:四个字节、float:四个字节、double:八个字节等等)的存储就涉及到存储顺序的问题了,因此标准规定了大端字节序存储和小端字节序存储两种形式(以哪种形式取决于编译器,如VS环境下就是小端字节序存储)
小端字节序存储:
将低地址的内容存到低地址处,高地址的内容存到高地址处。
大端字节序存储:
将高地址的内容存到低地址处,低地址的内容存到高地址处。
具体表现:
下面以vs环境下小端存储的形式进行演示:
从上图中不难看出:a中原本处于低地址的44存到了高地址处,原本处于高地址处的11存到了低地址处,这就是小端存储,如果当前环境是以大端存储的形式,情况则会相反。
判断当前环境下的存储方式:
#include<stdio.h>
int mian()
{
int n = 1;
int* p1 = &n;
char* p2 = (char*)p1;
if (*p2 == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
在以上程序中,如果编译器是小端存储,1会存储在低地址处,当char*的指针访问一个字节时,则会得到放在低地址处的1,从而判断出是小端存储;反之,得到0,从而判断出是大端存储。
浮点数在内存中的存储:
先举一个浮点数由十进制转为二进制以及科学计数法的例子:
3.5 //十进制
0000000000000000000000000000011.1 //二进制 (注意小数点后每一位的权重是2^-1 、2^-2……)
1.11 * 2^1 //科学计数法的形式
而规定浮点型在内存里如何存储的标准就是在科学计数法的基础上规定的,具体形式如下
-1^S*M*2^E //S为符号位,取0为正,取1为负
//M为有效数字
//E为指数位
具体的存储方式:
数据存入的细节:
1:对于M
由于科学计数法小数点前都是一个1,因此在存入时不管这个1,多出的一个字节用于存放更多的有效数字来提高精度。
2:对于E
E是一个无符号整数,取值范围为0到256,但指数的确有负数的存在,因此标准规定了一个中间值(float型为127,double型为1023),对每一个存入的E加上这个值,就避免了形式上出现负数的情况。
数据取出的细节:
1:E不全为1或0:
将指数位E减去中间数,并将M的小数点前补上1。
2:E全为1:
此时表示+-无穷大(+和-取决于符号位)。
3:E全为0:
此时浮点数已经非常的小,将指数位E减去中间数后,不在M的小数点前补上1.