数据的储存

  • Post author:
  • Post category:其他


数据的存储在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的取值来帮助判断。

最后的解答为:




相信你,翻过这座山会看到更广阔的天空!



版权声明:本文为m0_61500331原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。