数据的存储
一、数据类型
分类:
整型家族
char//字符型的本质就是ASCII值,所以归为整型家族
unsigned char
signed char
short
unsigned short int
signed short int
int
unsigned int
signed int
long
unsigned long int
signed long int
long long
unsigned long long int
signed long long int
1. 生活中有些数据是没有负数的,比如身高体重等,因此有unsigned的出现
2. 有符号数据的二进制序列包含两部分:第一位为符号位,0表示正数,1表示负数,其余为数值位,无符号数据的二进制位全部用于表示二进制位
浮点型家族
只要是小数就可以用浮点型来表示
double的精度更高,存储的数据范围更大,float的精度更低,存储的数据范围更小
long double //扩展双精度浮点型
double //双精度浮点型
float //单精度浮点型
构造类型
也叫自定义类型
数组类型
结构体类型(struct)
枚举类型(enum)
联合类型(union)
指针类型
int* p
char* p
float* p
void* p
空类型
void表示空类型(无类型)
通常应用于函数返回值类型,函数参数,指针类型
二、整型在内存中的存储
1. 整数有三种二进制表示方法:原码、反码、补码
2. 三种表示方法都有符号位和数值位。符号位的“0”表示正数,“1”表示负数,数值位根据数值转换成二进制序列即可
3. 正数的原码,反码,补码都相同,负数的原码,反码,补码各不相同,转换方法如下:
原码: 按照正负数的形式将数值转换成二进制序列
反码: 符号位不变,将原码的其他位按位取反
补码: 反码加1
4. 负数的原码和补码相互转换的方法是一致的
5. 整型数据在内存中存储的是补码
三、大小端字节序
大端模式: 内存的低地址存储数据的高字节,内存的高地址存储数据的低字节
小端模式: 内存的低地址存储数据的低字节,内存的高地址存储数据的高字节
原因: 字节是数据存储的基本单元,多字节数据存储需考虑字节在内存中的存放顺序,因此出现大小端模式
例:设计一个程序判断当前机器的字节序
#include <stdio.h>
int check_sys(int n)
{
char* p = (char*)&n;//将整型变量n的地址转换成一个字节的地址,即取n的首字节的地址存放在指针p中
return *p;//返回首字节的内容
}
int main()
{
int n = 1;//用十六进制表示为:0x00000001
int ret = check_sys(n);
if (ret == 1)//结果为1时表示首字节的内容为01
{
printf("小端字节序\n");
}
else
{
printf("大端字节序\n");
}
return 0;
}
四、浮点型在内存中的存储
浮点数的存储遵循IEEE754规则,其形式为
(-1)^S * M *2^E
S表示符号位,若该浮点数为正数,则S=0,若为负数,则S=1
M表示有效数字,满足1<=M<2
E表示指数位,与二进制序列中通过科学计数法所移的位数有关,可类比十进制的科学计数法
例:
十进制的5.0,
将其转换成二进制的101.0
5.0为正数,S=0,将101.0用科学计数法表示为1.01*2^2,则E=2,有效数字M=1.01
IEEE754规定浮点数在内存中的存放:
对于32位的浮点数,在内存中,S占用1个bit位,E占用8个bit位,M占用23个bit位
对于64位的浮点数,在内存中,S占用1个bit位,E占用11个bit位,M占用52个bit位
32位的浮点数存储模型如下:
对于有效数字M和指数E,还有特别的规定:
M: 由于1<=M<2,M始终是一点几的数,因此在内存中只需存入小数点后的二进制位,再补充0直至填满23个bit位,这样就节省了一个有效数字
例:有效数字M=1.01,只需存入01,再补充0满足23位
E: 1. 指数E是一个无符号整数,这意味着8位的E的取值范围为0~255,11位的E取值范围为0~2047
2. 但科学计数法中可以存在负指数,因此需要将E的真实值加上一个中间数得到计算值,再将这个计算值E转换成二进制序列存入内存。
3. 对于8位的E,这个中间数为127,对于11位的E,这个中间数为1023
例:真实值E=2时,计算值E=2+127=129
浮点数从内存中取出:
1. E既有0又有1
将二进制的E转换成十进制,真实值E=计算值E-127,再将有效数字M前加上第一位的1
例:0.5的二进制存储形式为 0 01111110 00000000000000000000000
01111110为十进制的126,真实值E=126-127=-1
00000000000000000000000补上前面的1为M=1.0
则该数的二进制为(-1)^0 * 1.0 * 2^-1,即0.1
转换成十进制就为0.5
2. E全为0
此时有效数字M不用补上前面的1,而是直接补上0,写成0.xxxxxxxxxx的小数
原因:E全为0,真实值E=-127,则M*2^(-127),这本身已经是一个很小的数了,接近于0
3. E全为1
此时认为该浮点数为+/-无穷大,+,- 号取决于符号位
原因:E全为1,真实值E=255-127=128,则M*2^128,表示趋于无穷大的数
看一个例子:
#include <stdio.h>
int main()
{
int n = 9;//以整数的形式存进去,正数的原反补码相同,补码为:00000000000000000000000000001001
float* pFloat = (float*)&n;//将整型数据的地址转换成浮点型数据的地址,以便能通过浮点型指针访问整型n
printf("n的值为:%d\n", n);//以整型的方式取出
printf("*pFloat的值为:%f\n", *pFloat);//以浮点型的方式取出,分为0 00000000 00000000000000000001001
//根据IEEE754规则取出数据得到该值趋向于0
*pFloat = 9.0;//通过指针改变n的值,以浮点型数据存储规则将9.0存进去,
//(-1)^0*1.001*2^3,S=0,E=130,M=1,001
//二进制序列为0 10000010 00100000000000000000000
printf("num的值为:%d\n", n);//以整型的方式取出
printf("*pFloat的值为:%f\n", *pFloat);//以浮点型的方式取出
return 0;
}
输出结果为: