数据的存储在C语言中无疑是一个难点,但是也要充满信心征服他的信心,
翻过这座山,会看到更广阔的天空。
基本数据类型
想要明白数据的存储首先要知道有哪些基本的数据类型。
基本数据类型有如下几种:
整型家族
整数家族包含int、sfotr、long还有char。
为什么会有char类型吗?它不是字符类型吗?
是因为字符在存储时就是ASCLL码值,所以在存储的时候被分为整型家族。
int、sfotr、long、char
又细分为有符号和无符号类型,一般情况下signed 可以省略不写。也就是 int 等价于 signed int
但是char 就比较特殊
char 不一定等于 unsigned char 也不一定等于signed char 这最终取决于编译器。
常见的编辑器
上就是char == signed char
浮点数家族
浮点数类型的存储和整型的存储截然不同,参考
浮点型在内存中的存储解析
float
double
自定义类型
数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型
指针类型就是各种类型的指针
int* p;
char*p;
float* p;
void* p;
空类型
void 表示空类型
常用于
函数的返回类型、函数的参数、指针类型
。
大小端字节序介绍和判断
在开始数据类型的存储前首先需要明白什么是大小端!
大小端字节序说明白就是
数据在电脑上存储的字节顺序。
我们能明白在计算机系统中,数据是以字节为单位的,每个地址单元都存放着一个字节。
而对于位数大于8 的处理器,例如16或32位的处理器,由于寄存器宽度大于一个字节,那么必然存在一个如何将多个字节安排的问题,因此就导致了大小端的存储模式
说人话就是 char 类型存储的时候(不考虑整型提升),放进去自然就不用考虑放置顺序,直接用就可以。但是int 类型不可以,一个int 类型就是4个字节也就是32比特位,放入后一个字节一个字节放置,需要考虑放置顺序,在众多存放顺序中最后就出现了两种存储模式:大端存储模式、小端存储模式。
大端储存(
大端字节序存储
)模式 :是指数据地位保存在内存的高地址中,而数据的高位,保存在内存的低地址中,
小段存储(
小端字节序存储
)模式:是指数据的地位保存在内存的低地址中,而数据的高位,保存在内存的高地址中,
比如我需要存储一个int i = 20;
换算为16进制 i = 0x14;
大小端在内存中的储存方式如下图:
而且我们平时用的编辑器一般都是小端存储。
怎么让计算机判断是大端存储,还是小端存储?
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void judgeEnds()
{
int a = 1; //a 的16进制位 0x00 00 00 01
if ((char*)&a) //通过取a的第一个字节来判断是不是大小端
{
printf("小端");
}
else
{
printf("大端");
}
}
int main()
{
judge(); //调用judgeEnds判断大小端
return 0;
}
整形在内存中的存储
整型在内存中的存储方式就是
补码
。
但是为什么是用补码呢?
1·使用补码,可以是符号位和数值域统一处理,防止0有两个编码。
2·让减法变成加法来处理(这是因为CPU只有加法器)。
3·补码于原码的相互转换条件是相同的,不需要额外的硬件电路。
正数的原码、反码、补码相同
int a = 10;
//00000000000000000000000001010 - 原码
//00000000000000000000000001010 - 反码
//00000000000000000000000001010 - 补码
负数的原码、反码、补码就有一定的差异了
int b = -10;
//10000000000000000000000001010 - 原码
//11111111111111111111111110101 - 反码 反码 = 除了符号位,其他位,按位取反
//11111111111111111111111110110 - 补码 补码 = 反码 + 1
现在我们计算一个10 – 10!
int a = 10;
int b = -10;
//如果用原码来计算的话 10 - 10 = 10 + (-10)
int c = a + b;
//00000000000000000000000001010 - a - 原码
//10000000000000000000000001010 - b - 原码
//10000000000000000000000010100 - c - 原码
//明显不对
//但是用补码就可以完美计算出10 + (-10) = 0
//00000000000000000000000001010 - a - 补码
//11111111111111111111111110110 - b - 补码
//00000000000000000000000000000 - c - 补码
浮点型在内存中的存储
浮点数的存储规则是根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)* S * M * 2^E
(-1)* S 表示符号位。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。
就拿5.5举例来说
5.5转换为2进制位就是 1 0 1 . 1
就可以写成 (-1)* 0 * 1.011 * 2 ^ 2
其中 M 、S 相当好理解,但是E就感觉有点疑惑
如果是0.75 写作
(-1)* S * M * 2^E
就是
(-1)* 0 * 1.1 * 2 ^
-1
对!问题就是-1 因为内存中存储的是二进制位,所以怎么可能会出现负数呢?
这时候就需要对E进行补充说明了:
E是一个无符号整数
E为8位就是 0~255 E为11位就是0~2047
但是科学计数法中的E可以出现负数。
所以存入内存时E的真实值必须再加上一个中间数 对于8位数时127 11位数是1023
E不全为0,不全为1
遵循中间值的守则该怎么算就怎么算。
E全为0
有效数字M不再加上第一位1,而是还原为0.xxxx的小数,这样做是为了表示+0,-0,以及很接近0的很小的数字。
E全为1
有效数字M全为0,表示+-无穷大。
说完S、M、E 后就是
float、double
类型的存储了
由上图可见,单精度双精度之间相差的可不是一点半点。
float S 1位 E 8位 M 23位
double S 1位 E 11位 M 52位
所以想要数字更准确,用double 类型还是好的。
练习
最后还有一个小练习:
int main()
{
int n = 16;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 16.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
提示:可以根据E的取值来帮助判断。
最后的解答为:
相信你,翻过这座山会看到更广阔的天空!