数据的存储在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的取值来帮助判断。
最后的解答为:
     
   
    
     相信你,翻过这座山会看到更广阔的天空!
    
   
 
