数据类型在内存中的存储
(一)类型的基本归类
(1)整形家族 char、int、short、long、long long、unsigned int等
(2)浮点型家族
(3)构造类型:数组、结构体、枚举、联合
(4)指针类型
(5)空类型:void 表示空类型(无类型),通常应用于函数的返回类型、函数的参数(不需要参数)、指针类型
整形的存储:
一个变量的创建是要在内存中开辟空间的,空间的大小根据不同的类型来决定
整形家族:存储是使用二进制 (原码,反码,补码)
内存中存储的是补码,原因:使用补码,可以将符号位和数值域统一处理,同时加法和减法也可以同时处理(CPU中只有加法器),此外,补码与原码相互转换,其运算过程是相通的,不需要额外的通行电路。
#include<stdio.h>
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;
}
这里会输出什么呢?
a=-1,b=-1,c=255
原因是:
在计算机中以补码存储
a,b都是有符号字符类型,在计算机中的补码为”11111111“,
当以”%d“的形式输出时,因为之前的字符类型有符号,所以转换成整形之后,仍然有符号,即”11111111111111111111111111111111“
再将其转换成原码,等于”-1“
而c是无符号字符类型,在计算机中的补码也为”11111111“,
当以”%d“的形式输出时,因为之前的字符类型无符号,所以转换成整形之后,仍然无符号,即”00000000000000000000000011111111“
再将其转换成原码,等于”255“
(二)浮点数在内存中的存储:
对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用 32bit,double数据占用 64bit.其实不论是float类型还是double类型,在计算机内存中的存储方式都是遵从IEEE的规范的,float 遵从的是IEEE R32.24 ,而double 遵从的是R64.53。
无论是单精度还是双精度,在内存存储中都分为3个部分:
-
符号位(Sign):0代表正,1代表为负;
-
指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储;
-
尾数部分(Mantissa):尾数部分
其中float的存储方式如下图所示:
而双精度的存储方式为:
R32.24和R64.53的存储方式都是用科学计数法来存储数据的
用二进制的科学计数法第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了 24bit,道理就是在这里。
那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数 点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了, 所以指数部分的存储采用移位存储,存储的数据为元数据+127。
下面就看看8.25和120.5在内存中真正的存储方式:
首先看下8.25,用二进制的科学计数法表示为
:1.0001*2^3 按照上面的存储方式,符号位为0,表示为正;指数位为3+127=130,位数部分为 1.00001,故8.25的存储方式如下: 0xbffff380: 01000001000001000000000000000000
分解如下:0–10000010–00001000000000000000000
符号位为0,指数部分为10000010,位数部分为 00001000000000000000000
同理,120.5在内存中的存储格式如下: 0xbffff384: 01000010111100010000000000000000
分解如下:0–10000101–11100010000000000000000
那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值 呢?其实就是对上面的反推过程,比如给出如下内存数据: 01000001001000100000000000000000
第一步:符号位为0,表示是正数; 第二步:指数位为10000010,换算成十进制为130,所以指数为130-127=3; 第三步:尾数位为01000100000000000000000,换算成十进制为 (1+1/4+1/64); 所以相应的十进制数值为:2^3*(1+1/4+1/64)=8+2+1/8=10.125。
(三)大小端模式:
大端存储:就是把一个数的低位字节序的内容存放到高地址处,高位字节序的内容存放在低地址处。
小端存储:就是把一个数的低位字节序的内容存放到低地址处,高位字节序的内容存放在高地址处。
那么如何检测自己的电脑是大端还是小端模式呢,下面举出几种方法
法一:利用共用体(联合union)来检测(常用)
#include <stdio.h>
int Check()
{
union Un
{
char c;
int i;
}un;
un.i = 1;
return un.c;
}
int main()
{
int ret=Check();
if (1 == ret)
{
printf("当前模式为小端存储\n");
}
else
{
printf("当前模式为大端存储\n");
}
return 0;
}
为什么用的是共用体来检测大小端而不是结构体来检测呢?
这是因为共用体特殊的底层结构,下面是共用体的模型:
可以看出来,char类型的变量和int 类型的变量共用的是同一块空间,我们给 i 赋为1 那整个空间都是 1 ,如果返回的是1,那就说明当前模式是小端存储,返回值是0 就说明当前为大端存储。
法二:利用指针
以前我们说过一个整形的指针每次偏移的是4个字节,而一个字符类型的指针每次偏移的是一个字节,那么我们就可以利用这个特性写一个程序来检验:
#include<stdio.h>
int Check()
{
int a = 1;
char* p = (char*)&a;
return *p; //大端返回0,小端返回1
}
int main()
{
int ret;
ret=Check(); //写一个测试的函数
if (1 == ret)
{
printf("当前模式为小端存储\n");
}
else
{
printf("当前模式为大端存储\n");
}
return 0;
}