C语言——深度刨析数据在内存中的存储

讲解重点:

  1. 数据类型详细介绍
  2. 整形在内存中的存储
  3. 大小端字节序及判断
  4. 浮点型在内存中的存储解析

一:数据类型介绍

1.1 数据类型
char字符数据类型
short短整型
int整形
long长整型
long long

更长的整形

float单精度浮点数
double双精度浮点数
1.2 类型的基本归类

整形家族

char

      unsigned char

      signed char

short

        unsigned short

        signed short

int

         unsigned int

         signed int

long

        unsigned long [ int ]

        signed long [ int ]

浮点数家族

float

double

构造类型

> 数组类型

> 结构体类型 struct

> 枚举类型 enum

> 联合类型 union

指针类型

int *p;

char *p;

float *p;

void *p;

二:整形在内存中的存储


||我们知道创建一个变量是要在内存中开辟空间的,而空间的大小是根据不同类型而决定的,

接下来我们就讨论一下整形数据在开辟的空间中是如何储存的

比如:

  int a = 20;

  int b = -10;

我们知道a和b都是分配4个字节的空间,那如何储存?

下面先来了解一下以下概念:

2.1 原码 反码 补码

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

三种表示方法均有符号位数值位两部分,符号位都是用0表示 ‘正’ ,用1表示 ’负‘,而数值位

  • 正数的原码反码补码都相同
  • 负整数的三种表示方法各不相同,如下

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

反码 :将原码的符号位不变,其余位取反就可以得到反码

补码 :反码+1就可以得到补码

                             


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

那为什么呢?

       在计算机系统中,数值一律用补码来表示和储存,原因在于使用补码,可以将符号位和数值域统一处理,我们举个例子:

int a=1;

int b=-1;

int c=a+b;

在内存中,如果用两个数的原码进行计算的话我们看看会发生什么:

a的原码:00000000000000000000000000000001

b的原码:10000000000000000000000000000001

那么a+b:10000000000000000000000000000001  ->该二进制表示-1 ,显然计算结果错误

接下来再用两个数的补码进行计算:

a的原码:00000000000000000000000000000001

a的补码:00000000000000000000000000000001

b的原码:10000000000000000000000000000001

b的反码:1111111111111111111111111111111111110

b的补码:1111111111111111111111111111111111111

      a+b: 00000000000000000000000000000000   (从低位往高位进一最后32位都为0,计算结果位0,计算正确)

2.2 大小端的介绍

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


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

那为什么会有大小端呢?

 这是因为在计算机系统中,我们是以字节为单位的,一个字节位8bit,像16bit的short类型,32bit的long类型,由于寄存器宽度大于一个字节,必然会存在如何将多个字节安排的问题,因此就出现了大小端存储模式

例如:

int a = 0x11223344;


                                    低地址                                                    高地址

大端字节序存储:                             11   22   33  44

小端字节序存储:                              44  33   22  11

2.3  例题讲解

了解了上面的概念以后,我们来实战几个例题:

#include <stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("%d %d %d", a, b, c);
	return 0;
}

想一下这个代码的结果是什么呢?

结果是:   -1    -1    255

接下来我来讲解一下他们的计算过程:

 解析:

       由于内存中储存的是数值的补码,所以我们先写出-1的补码

-1: 原码:10000000000000000000000000000001

        反码:1111111111111111111111111111111111110

        补码:1111111111111111111111111111111111111

     由于char类型占1个字节,也就是8个bit位,所以a b c只能保存到后8位,即11111111

而%d是10进制的形式打印有符号数,并且字符型在进行整数运算时要进行整型提升(不了解的同学可以看我前面写的 ---  操作符详解(2)---)

    char类型和signed char类型都有符号位,在进行整型提升时高位补充符号位

也就是  11111111111111111111111111111111 ,我们认为他是有符号数,而符号位为1代表负数,为了更加清晰的看出上面数字的大小,我们再把他转化为原码:

注:补码转换原码:符号位不变其他位按位取反加一

             1000000000000000000000000001 --计算结果为 -1

    unsigned char 类型不包含符号位,在整形提升时,高位补0,

             00000000000000000000011111111,它的符号位为0,代表正数,所以他的原码和补码相同,计算结果为255

       

#include <stdio.h>
int main()
{
	char a = -128;
	char b = 128;
	printf("%u %u", a,b);
	return 0;
}

想想这个代码的结果又是什么呢?

结果:     4294967168       4294967168

解析:

     方法和上题类似,我们先写出他们的补码

-128:

     原码:10000000000000000000000010000000

     反码:1111111111111111111111111111011111111

     补码:111111111111111111111111111110000000

char类型占8个bit,所以a中存的是10000000,

我们给他整型提升一下,高位补符号位11111111111111111111111110000000,

由于我们是按%u的形式打印,也就是我们认为a是一个无符号数,对于无符号数来说原码反码补码相同,最高位的1也是有效位,我们看看这么大的数字再十进制下是多少:

可以看出这就是我们的答案了

同理:

128:

     原码:00000000000000000000000010000000

     补码:00000000000000000000000010000000

此时b中存储的也是10000000,整型提升完以后还是1111111111111111111111111000000,

所以计算结果和上面相同

2.4 char类型详解

对于signed char

一个字节,8个bit位,那么char类型可以储存的数字的范围是多少呢?

00000000     0           //符号位为0,补码和原码相同

00000001     1

.........

01111111      127

10000000     -128

10000001     -127

....

111111110      -2

111111111      -1        //符号位为一,转化为原码,符号位不变其他位取反再加一

有上述分析可知,signed char类型可以表示的范围是 -128~127

当11111111再加1时,由于char类型只能保存8个bit,再加1后会丢失数据,加1后会变为 00000000

对于unsigned char

00000000     0

00000001     1

......

01111111      127

10000000     128

10000001     129

....

11111110        254

11111111        255     //每位都是有效位

signed char可以表示的范围是 0~255

例如:

#include <stdio.h>
int main()
{
	unsigned char i = 0;
	for (i = 0; i <= 255; i++)
	{
		printf("hello\n");
	}
	return 0;
}

我们思考一下这个代码为什么会死循环打印hello,本来当i加到256不符合条件时就应该终止循环,由于无符号字符型可以取到的范围是0~255,255即11111111,再加一后丢失数据变为00000000,即i会从255变为0,还会继续循环,所以会一直死循环

这样的思想可以类似于short     int等等

short  -2个字节-16个bit

signed short :-32768~32767

unsigned short :0~65535

注:所以当以后遇到无符号数的计算一定要仔细思考!!!

三:浮点型在内存中的存储

浮点数在内存中是怎样存储的呢?我们举个例子:

#define  _CRT_SECURE_NO_WARNINGS 1	
#include <stdio.h>
int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n",n);
	printf("*pFloat的值为:%f\n", *pFloat);

	*pFloat = 9.0;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);

	return 0;
}

答案是不是出乎你的意料呢?

从这也可以看出,浮点型和整形 数据的存储方式是不一样的,接下来我介绍一下浮点型数据在内存中的存储

根据国际标准IEEE(电子和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:

  • (-1)^S*M*2^E
  • (-1)^S表示符号位,当S=0,V为正数,当S=1,V为负数
  • M表示有效数字,大于等于1,小于2
  • 2^E表示指数位

举例来说:

十进制的5.0,写成二级制为:101.0 ,相当于1.01×2^2.

按上面的形式,可以得出S=0,M=1.01,E=2

 

IEEE 754对有效数字M和指数E,还有一些特别规定。


前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。


至于指数E,情况就比较复杂。


首先,E为一个无符号整数(unsigned int)
这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即
10001001。


然后,指数E从内存中取出还可以再分成三种情况


E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
比如:
0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为
1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:

0 01111110 00000000000000000000000

E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
0 01111110 00000000000000000000000
E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);

接下来我们就分析一下刚开始那个题:

9的原码:00000000000000000000000000001001

当我们以整形数据存储以float类型取的时候,0 00000000 00000000000000000001001

S=0     E=00000000  M=0. 00000000000000000001001

即取的结果:(-1)^0*(0. 00000000000000000001001)^-126 是一个无限趋近于0的数,

所以打印结果为0.000000

当我们以float类型存储以int类型取得时候,9写为二级制可以表示为1.001*2^3

存到内存中即为 0 10000010 00100000000000000000000当我们以整形数据看待他是他是一个很大的正数

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张呱呱_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值