C语言提供了6种最基本的数据类型,分别是:short
、int
、long
、float
、double
、char
。其中数值类型为short
、int
、long
、float
、double
,字符类型为char
。下面我们对其进行一些详细介绍。
存储整数如1、-78、653类数字。
短整型
short
,所占存储空间大小为2个字节,
存储的数字范围在-215 ~ 215-1。
整型
int
,所占存储空间大小为4个字节,
存储的数字范围在-231~ 231-1。
长整型
long
,所占存储空间大小为8个字节(64位机器),
存储的数字范围在-263 ~ 263-1。
用来存储小数类型的数字,如3.1415、2.08之类的数字
单精度类型
float
,所占存储空间大小为4个字节,
存储的数字范围在-3.4x10-38 ~ 3.4x1038。
双精度类型
double
,所占存储空间大小为8个字节,
存储的数字范围在-1.7x10-380 ~ 1.7x10380。
用来表示字符的类型,如A、e、\n等等。
字符型
char
,所占存储空间大小为1个字节,
存储的范围在-128 ~ 127。
虽然 char 被单独抓出来独立为一个类型,但本质上还是属于整数类型。这是因为计算机只能识别二进制码,因此 char 实际存储的值是数字,而不是字符。
在ASCII码中,一共有 128 个数字,对应了 128 个字符。
在计算机中整数的二进制有3种表示方法,原码,反码,补码。
对于无符号位来说,三种表示方法均由“数值位”组成。
对于有符号位来说,三种表示方法都由“符号位”和“数值位”两部分组成,符号位为最高位,用0表示“正”,用1表示“负”。
对于整形来说,在内存中存放的其实是补码。
针对无符号数,其原码、反码、补码均相同。
针对有符号数,在其为正数的情况下,原码、反码、补码相同;在其为负数的情况下,反码为除了符号位剩余都要取反,补码为反码+1。
int a = 10;
//10
//求原码:它的二进制序列为1010,因为是正数,符号位是0,则原码的最前面是0,其余位置补上0
//00000000 00000000 00000000 00001010 - 原码
//00000000 00000000 00000000 00001010 - 反码
//00000000 00000000 00000000 00001010 - 补码
//转换成十六进制为0x00 00 00 0a
利用vs,我们观察到在内存中,其形式为
int b=-10;
//-10
//求原码:10的二进制序列为1010,而-10是负数,符号位是1,则原码最前面是1,其余位置补上0
//10000000 00000000 00000000 00001010 - 原码
//11111111 11111111 11111111 11110101 - 反码(原码取反)
//11111111 11111111 11111111 11110110 - 补码(反码+1)
//转换成十六进制为0xff ff ff f6
利用vs,我们观察到在内存中,其形式为
在内存中我们存储的是补码,运算也是按照补码来运算(注意不要超出数据类型的范围)。但其实输出时,我们会按照原码输出,而从补码到原码,和从原码到补码,其步骤是相同的,都是取反加一。
我们以-1为例,看一下如何从补码到原码。
原码 10000000 00000000 00000000 00000001 - 原码
取反 11111111 11111111 11111111 11111110 - 反码
加一 11111111 11111111 11111111 11111111 - 补码
补码 11111111 11111111 11111111 11111111 - 补码
取反 10000000 00000000 00000000 00000000 -补码取反并不是反码,要注意这一点
加一 10000000 00000000 00000000 00000001 - 原码
上面讲了整数在内存中的存放,那么小数如何在内存中存放呢?
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数可以表示成下面的形式:
(-1)S * M * 2E
S(阶符) 表示符号位,当数值为正数时,S=0,负数时,S=1。
M(尾数) 表示有效数字 (1≤M<2)。
2E(阶码) 表示指数位。
二进制的小数点权位如下
小数表示 | 阶码 | 二进制小数点权位 |
---|---|---|
0.5 | 2-1 | 0.1 |
0.25 | 2-2 | 0.01 |
0.125 | 2-3 | 0.001 |
0.0625 | 2-4 | 0.0001 |
0.03125 | 2-5 | 0.00001 |
0.015625 | 2-6 | 0.000001 |
… | … | … |
根据上面的表格,我们给出几个例子:
float a = 1.5;
它的二进制为:1.1
根据 (-1)S * M * 2 E
a = (-1)0* 1.1 * 2 0
又比如 -0.5 = (-1)1* 1.0* 2-1
对于浮点数,只有S,M,E不一样,所以只要存储这三个数就行
对于单精度浮点数float
和双精度浮点数double
规定如下:
对S的规定
S表示正负,0为正,1为负。
对M的规定:
为保证 1≤M<2 ,M可以写成 1.xxx 的形式,其中xxx表示小数部分。所以在计算机内部保存M时,默认这个数的第一位总是1,因此这个1可以被舍去,只保存后面的 xxx 的小数部分。比如保存1.01的时 候,只保存01,等到取出时,再把第一位的1加上去。这样做是为了节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
对E的规定:
E为一个无符号整数(unsigned int),如果E为8位,它的取值范围为0 ~ 255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E的真实值是10,所以保存32位浮点数时,必须保存成10+127=137,即 10001001。
取出E时的三种规则
1.E不全为0或不全为1
此时浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
如:0.5的二进制形式为0.1,由于规定正数部分必须为1~2,即将小数点右移1位,则为 1.0*2-1,其阶码为-1+127=126,表示为 01111110,而尾数1.0去掉整数部分为0,补齐0到23位,则其二进制表示形式为:
float c=0.5;
//二进制存储形式为0 01111110 00000000000000000000000
//转换成十六进制为0x3f 00 00 00
利用vs,我们观察到在内存中,其形式为
再如:-4.5:
4.5的二进制形式为100.1,由于规定,正数部分必须为1~2之间的数,小数点左移2位,则为1.001* 22,其阶码为2+127=129,即表示为10000001,而尾数部位去掉整数部分为001,补齐到23位,则二进制表达形式为:
float d=-4.5;.
//二进制存储形式为1 10000001 00100000000000000000000
//转换成十六进制为0xc0 90 00 00
利用vs,我们观察到在内存中,其形式为
2.E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值, 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
3.E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
在整形和浮点型数据的存储中,我们分别给出了十六进制的表达形式和在内存中存储的形式,可以看到,在vs中,内存存储形式并不是我们认为的十六进制的形式。
如0x12 34 56 78,其中12为高地址,78为低地址
在vs的内存存储中会以0x78 56 34 12的形式展示给我们看,即先存低地址,再存高地址。
其实只要存取方便,怎么存储都是可以的,下面介绍两种存储字节序:
大端字节序存储:
是指数据的低位字节的内容保存在内存的高地址中,而数据的高位字节的内容,保存在内存的低地址中。
小端字节序存储:
是指数据的低位字节的内容保存在内存的低地址中,而数据的高位字节的内容,保存在内存的高地址中。
下面的代码可以帮助大家判断自己的机器是按大端存储还是小端存储:
int check_sys()
{
int a = 1;
return *(char*)&a;
}//小端:返回1
//大端:返回0
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
以上就是有关整形和浮点型数据在内存中如何存储、大小端字节序讲解的所有内容,希望对您的学习有所帮助。