【C 数据存储详解】(1)——深度剖析整形数据在内存中的存储

举报
YIN_尹 发表于 2023/08/03 17:24:47 2023/08/03
【摘要】 今天我们一起来学习一下C语言中的整型数据是如何在内存中存储的!!!一.数据类型介绍1.类型的意义我们已经学习过了一些基本的内置类型:char //字符数据类型short //短整型int //整形long //长整型long long //更长的整形float //单精度浮点数double //双精度浮点数以及他们所占存储空间的大小(单位是字节):#include <stdio.h>int ...

今天我们一起来学习一下C语言中的整型数据是如何在内存中存储的!!!


一.数据类型介绍

1.类型的意义

我们已经学习过了一些基本的内置类型:


char //字符数据类型

short //短整型

int //整形

long //长整型

long long //更长的整形

float //单精度浮点数

double //双精度浮点数


以及他们所占存储空间的大小(单位是字节):

#include <stdio.h>
int main()
{
    printf("%d\n", sizeof(char));
    printf("%d\n", sizeof(short));
    printf("%d\n", sizeof(int));
    printf("%d\n", sizeof(long));
    printf("%d\n", sizeof(long long));
    printf("%d\n", sizeof(float));
    printf("%d\n", sizeof(double));
    printf("%d\n", sizeof(long double));
    return 0; }

d0933bd8e0734138bfea1050853ed15a.png

C语言规定了这么多不同的数据类型,那么它们有什么意义呢?


1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。

2. 如何看待内存空间的视角。

2.类型的基本归类

下面我们对C语言中的数据类型做一个基本的归类:

(1).整型家族


1.char

unsigned char

signed char


2.short

unsigned short [int]

signed short [int]


3.int

unsigned int

signed int


4.long

unsigned long [int]

signed long [int]


这里大家可能会有疑惑,char不是字符类型吗?为啥归到整型里面了。


这是因为每个字符都有对应的ASCII码值,这些字符在内存中存储的时候,实际就是存放的ASCII码值,而ASCII码值都是整数,所以将char也归类到整型里面了


在这里还要给大家提醒一点,就是:


对于char来说,C语言本身并没有明确规定我们定义一个char类型的变量,它到unsigned char 还是 signed char 。 这个取决于编译器,不同的编译器情况可能不同,但是在绝大多数编译器上都是signed char 。

但是,对于short,int,long ,C语言规定了

1.short 就是signed short

2.int 就是 signed int

3.long 就是 signed long


(2).浮点数家族


float 单精度浮点型

double 双精度浮点型


(3).构造类型


数组类型

结构体类型 struct

枚举类型 enum

联合类型 union


(4).指针类型


int pi;

char pc;

float pf;

void pv;

0df6da820ea54658a3f0aa523d65778a.png

(5).空类型


void 表示空类型(无类型)

通常应用于函数的返回类型、函数的参数、指针类型


二.详解整型在内存中的存储

我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。


那接下来我们谈谈数据在所开辟内存中到底是如何存储的?

比如:

int a = 20;
int b = -10;

我们知道为 a 分配四个字节的空间。

那如何存储?

来了解下面的概念:


1. 原码、反码、补码

计算机中的整数有三种2进制表示方法,即原码、反码和补码。

三种表示方法均有符号位和数值位两部分:

符号位都是用0表示“正”,用1表示“负”。

而数值位:

正数的原、反、补码都相同。

负整数的三种表示方法各不相同。


下面来介绍一下什么时是原码、反码、补码:

原码:


直接将数值按照正负数的形式翻译成二进制就可以得到原码。


补码:


将原码的符号位不变,其他位依次按位取反就可以得到反码。


反码:


反码+1就得到补码。


举个例子:

f00415cb0880456c80c05937fd6d8377.png

再看一个负数:

c9ba3f76ad544acdb98724c2493dac35.png

整数的2进制表示方法有原码、反码和补码,那内存中存的到底是啥哪?


对于整形来说:数据存放内存中其实存放的是补码。


为什么呢?


在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;

同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的

,不需要额外的硬件电路。


下面来解释一下:


1. 可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理


因为CPU只有加法器,所以对于1-1这样的表达式CPU要处理成1+(-1)来进行计算的。

而如果直接将两个操作数的原码进行相加,是可能会出错的:

举个例子:

1a8753b321574cf68b1df88e43bf7c74.png

1+(-1),我们用原码相加,得到错误的结果

用补码计算:

f2106145e58e4debb3e6c092bf93b022.png

最终用补码相加得到的结果才是正确的!!!


2.补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。


我们通过原码得到补码的方法是:

原码的符号位不变,其它位按位取反得到反码,反码加1,得到补码;

其实补码转到原码也可以用同样的方法。


然后我们创建两个变量,看一下,内存给我们展示出来的是什么样子的:

db0b1f1bd122469da0769102e3bb6ce2.png

我们可以看到对于a和b分别存储的是补码。但是我们发现顺序有点不对劲,好像是相反的。

这是又为什么?


2.大小端介绍

上面我们发现,对于a和b分别存储的是补码。但是我们发现顺序有点不对劲,好像是相反的,为什么?


学完下面的内容,我们就清楚了。

什么大端小端:


大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;


小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中


解释一下:

5badc71ca232421c9523aa2dae23800f.png

现在再看这张图我们就明白为什么顺序有点不对劲了。

4926b95dded643e5bfe618365d5d39f2.png

因为在vs2022上,采用的是小端存储模式。


那为什么会有大小端呢?


为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元

都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。


例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。


3.百度2015年系统工程师笔试题讲解

那么我们接下来做一道练习题,这道题是百度2015年系统工程师笔试题:


请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。


概念我们上面已经说过了,那怎么设计程序呢?我们来思考一下:


我们可以用整数1来帮助判断,取出1的第一个字节的内容,1的补码是:00000000000000000000000000000001,16进制是:00 00 00 01;

如果第一个字节的值是0(高位在低地址),则为大端;

如果第一个字节的值是1(低位在低地址),则为小端。

(注:我们取出的第一个字节是处在低地址的那一个字节)

afefa61b4ef842408d7feda9c454f1a0.png

上代码:

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

我们已经知道了vs上是小端,我们来一下看结果对不对:

72436107aa964b1f86dd1da538bfcca1.png

以上就是对整型在内存中如何存储的详细介绍,欢迎大家指正,我们一起进步!!!

00f04406d8ba4137945e709d108e6f55.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。