数据在内存中的存储

前言

        对于数据在内存中的存储,在上一篇博客《C语言中的库函数》中有提到过一部分。这篇博客将会更详细的介绍,除了整数之外的浮点数存储形式。浮点数的储存真的复杂许多,对于新手来讲着实不太友好,再加上计算机是二进制存储,所以想要更细致的判断从存储格式到实际显示格式,对于无法整确被2进制解析的将会非常困难,比如说3.14。只能说设计者实在是太狠了,一般人一眼看不出来。

一. 整数

1. 整数的储存方式

        在计算机中,整数以二进制在数据内存中储存。其中,最高位是符号位,0为正,1为负。数据以补码的形式储存,对于正数来说,原码就是补码。对于负数来说需要先转换为反码,再转换为补码。实际变换规则如下:

int a = -1;
//原码:10000000 00000000 00000000 00000001
//符号位不变,其他位取反
//反码:11111111 11111111 11111111 11111110
//+1
//补码:11111111 11111111 11111111 11111111

//如果想得到原码
//补码:11111111 11111111 11111111 11111111
//符号位不变,其他位取反
//10000000 00000000 00000000 00000000
//+1
//原码:10000000 00000000 00000000 00000001

        看了这段代码不经有一种疑惑,为什么在的到原码的时候不将补码-1得到反码,再取反得到原码呢?这当然是可以的,只是这样子取反码的过程不是必要的,只记得取反、+1一种顺序也会有相同的效果。

2. 数据的显示

        在调试窗口下,数据会以16进制的方式显示。例如:

int a = 0x11223344;

        它的储存方式为44 33 22 11。

3. 储存顺序

        数据在电脑中储存的方式有大端存储和小端存储两种形式,它们分别为:

(1) 大端存储

        大端存储就是低地址存高位数据,高地址存低位数据。

//例如
int a = 0x11223344;
//      a
//      | <-表示a地址
//      11 22 33 44
//低地址---------------------------------高地址

(2) 小端存储

        小端存储就是低地址存低存位数据,高地址高位数据。

//例如
int a = 0x12345678;
//      a
//      | <-表示a地址
//      78 56 34 12
//低地址---------------------------------高地址

        那么数据存储就不能不按照这样的顺序存吗?按照其他的顺序大概是可以的,不过这种机器肯定是很稀有的,乱序不是那么好读取,所以这两种方式是比较主流的。

(3) 判断数据存储的方式

        通过一段简单的代码就能判断你的电脑里是什么方式储存数据的。

#include <stdio.h>
 
int main()
{
    int a = 1;
    char* p = (char*)&a;
    if(*p == 1)
    {
        printf("小端存储\n");
    }
    else
    {
        printf("大端存储\n");
    }
    return 0;
}

        利用的原理就是用char*接收a的地址,如果是大端难么*p==0,反之*p==1。

        我使用的vscode输出的是小端存储:

二. 浮点数

1. 浮点数的表现形式

        浮点数主要有两种写法,一种是小数形式,例如:3.14。另一种是指数形式,例如:2E10,它的意思是2*10^10.

2. 浮点数的储存方式

        浮点数和整数的储存方式大有不同,下面将给出一个例子展示给大家。

#include <stdio.h>

int main()
{
    int n = 9;
    float *f = (float*)&n;
    printf("整数形式打印n:%d\n", n);
    printf("浮点数形式打印n:%f\n", n);
    *f = 9.0f;
    printf("整数形式打印n:%d\n", n);
    printf("浮点数形式打印n:%f\n", n);
    return 0;
}

        运行结果如下:

        通过以上例子大概就能明白,浮点数和整数的存储是有很大不同的,特别是在吧9.0f存到n里面打印整数的时候得到了1091567616的答案。

        通过最新的标准,浮点数的存储规则如下:

V = (-1)^S * M * 2^E;

        其中S表示为符号位,M表示有效数字,E表示指数位。

(1) float

        对于32为的浮点型来说,S占1个字节,E占8个字节,M占23个字节。

//例如
// 00000000 00000000 00000000 00000000
// 这个数据会被分为
// 0 00000000 00000000000000000000000
// S     E              M

(2) double

        double占64个字节,S占1个字节,E占11个字节,M占52个字节。

//double
//例如
// 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
// 这个数据会被分为
// 0 00000000000 0000000000000000000000000000000000000000000000000000
// S      E                               M

3. 储存规定

        对于M来说,M的范围为, M >=1 && M < 2,那么因为M的表示方法必然为“1.”……,所以直接默认最高位的1,这样能够提高浮点数M多了一位,精度提高了。

        对于E来说,因为需要E有正负,而正负数在不加符号位时不好表示,为了提高效率,编译器会默认对于float型,E的值等于实际的大小+127。对于doublet型,E的值等于实际的大小+1023。也就是说如果E == 0,那么E的部分会分别被置为01111111和01111111111。

        实际的理解还需要举例,接下来给出一个特殊的数字,体验一下浮点数的存储规则吧。

float f = 5.5f;
//转换为2进制->101.1
//提取指数部分->1.011*2^2
//转换为标准形式->(-1)^0 * 1.011 * 2^2
//最后还原为存储方式 S = 0, M = 011, E = 2 + 127
//结果为:
// 0 10000001 01100000000000000000000
// S     E              M

        那么现在来解释一下为什么之前打印n的时候出现了一个特别大的数。

float f = 9.0f;
//转换为2进制->1001
//提取指数部分->1.001*2^3
//转换为标准形式->(-1)^0 * 1.001 * 2^3
//最后还原为存储方式 S = 0, M = 001, E = 3 + 127
//结果为:
// 0 10000010 00100000000000000000000
// S     E              M

        实际上,将这个数以%d转换为十进制整数打印就刚好能得到109157616这个数。

作者结语

        对于数据结构而言,浮点型整数型的存储只是占了一小部分。实际上还包括结构体。其中线性数据结构有:顺序表,链表,栈,队列。树形数据结构还包括二叉树。等等内容。这些内容将会在下一篇博客之中介绍,这里先预告一下。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值