数据在内存中的存储方式

数据于内存中的存储方式

在之前的学习中,我们学习了许许多多的数据类型,而今天我们就来系统地学习关于数据类型的知识。

1. 基本数据类型的分类

所有的数据类型大体可以分为以下几个类型,它们的具体成员如下:

1. 整形家族:

char 
	unsigned char
	signed char
short 
	unsigned short
	signed short
int 
	unsigned int 
	signed int 
long
	unsigned long
	signed long

2. 浮点数家族:

float
double

3. 构造类型:

数组类型
结构体类型:struct
枚举类型:enum
联合类型:union

4. 指针类型

int* pi;
char* pc;
float* pf;
void* pv;

5. 空类型:

void 表示空类型(无类型)
通常用于函数的返回类型,参数,指针类型

2. 整形数据在内存中的存储方式

在我们之前的学习中,我们知道每创建一个变量都要在内存中开辟一块对应的空间,空间的大小的由数据的类型的决定,而接下来我们将学习不同数据类型在内存中的存储方式。

2.1 原码,反码与补码

在计算机中整形的表示方式有三种,原码,反码,补码
这三种表示方式,具由两部分组成,符号位数值位,符号位上0 代表"正",1 代表"负",数值位上正数与负数的表示方式不同,具体如下:

  1. 原码
    符号位按正负区分,数值位正负数都为直接将数据转换为二进制形式存储
  2. 反码
    正数的反码与原码相同,负数的反码为原码符号位不变,数值每一位按位取反即可
  3. 补码
    正数的补码亦与原码相同,负数的补码为反码加一

对于整形家族的数据,它们于内存中都是以补码的形式存储的。这样存储的原因在于,二进制数无法直接进行负数的表示,而使用补码,可以将符号位于数值一同处理,同时,加法于减法运算也可以作统一处理,不需要额外的硬件电路。

示例:

int main()
{
	int a = 20;
	//16 + 4
	//2的4次方 + 2的2次方
	//原码:0000 0000 0000 0000 0000 0000 0001 0100
	//反码:0000 0000 0000 0000 0000 0000 0001 0100
	//补码:0000 0000 0000 0000 0000 0000 0001 0100
	//14 00 00 00
	int b = -10;
	//原码:1000 0000 0000 0000 0000 0000 0000 1010
	//反码:1111 1111 1111 1111 1111 1111 1111 0101
	//补码:1111 1111 1111 1111 1111 1111 1111 0110
	//f6 ff ff ff
	//内存监视窗口以十六进制显示数据
	//内存中最小的单位为字节(8bit)

	return 0;
}

在这里插入图片描述
在这里插入图片描述

2.2 大端与小端

大端存储法与小端存储法:

  1. 大端存储法:数据的低数值位存于低地址的内存空间,高数值位存于高地址的内存空间
  2. 小端存储发:数据的高数值位存储于低地址,低数值位存储于高地址

大端与小端存储法出现的意义:
我们已知,内存空间的是一个一个基本存储单位构成的,而这一个个基本存储单元的大小为1个字节。
当我们需要存储的数据长度过长时,一个字节的大小并不足以存放数据,这时就会有调用新空间来存放数据的必要,这样究竟是调用高地址或低地址的空间。因此,就产生了大端与小端存储法的分别。

练习:(编写一段代码,判断当前内存空间采用的存储方法)

//方法1:
int main()
{
	int i = 1;
	char j = *((char*)&i);
	//低地址处的值
	if (j == 1)
	{
		printf("小端存储");
	}
	else
	{
		printf("大端存储");
	}

	return 0;
}

//方法2:
union a
{
	int i;
	char j;
}x;

int main()
{
	x.i = 1;
	if (x.j == 1)
	{
		printf("小端存储");
	}
	else
	{
		printf("大端存储");
	}

	return 0;
}

2.3 整形数据存储练习题

输出结果:

//题目1
int main()
{
	//在内存中数据都是以补码的方式存储的
	//而数据的类型决定了读取它们的方式
	//数字的类型默认为int
	//当存储进入的变量空间不够时,会进行数据的截断
	char a = -1;
	//1字节
	//原码:1000 0000 0000 0000 0000 0000 0000 0001
	//反码:1111 1111 1111 1111 1111 1111 1111 1110
	//补码:1111 1111 1111 1111 1111 1111 1111 1111
	//存储:1111 1111
	signed char b = -1;
	//原码:1000 0000 0000 0000 0000 0000 0000 0001
	//反码:1111 1111 1111 1111 1111 1111 1111 1110
	//补码:1111 1111 1111 1111 1111 1111 1111 1111
	//存储:1111 1111
	unsigned char c = -1;
	//补码:1111 1111 1111 1111 1111 1111 1111 1111
	//存储:1111 1111
	printf("a = %d, b = %d, c = %d", a, b, c);
	//整形提升:有符号数高位填充符号位,无符号数填充0
	//以十进制有符号的方式打印,打印数据的原码,会进行整型提升(char -> int)
	//a:-1 b:-1 c:255
	
	return 0;
}

//题目2
int main()
{
	char a = -128;
	//1000 0000 0000 0000 0000 0000 1000 0000
	//截断:1000 0000
	//反码:0111 1111
	//补码:1000 0000
	printf("%u\n", a);
	//以无符号十进制的方式打印数据
	//1111 1111 1111 1111 1111 1111 1000 0000
	//4,294,967,168
	//直接打印
	
	return 0;
}

//题目3:
int main()
{
	char a = 128;
	//1000 0000
	//最高位默认为符号位
	//整形提升:1111 1111 1111 1111 1111 1111 1000 0000
	printf("%u", a);
	//同上

	return 0;
}

//题目4:
int main()
{
	int i = -20;
	//1000 0000 0000 0000 0000 0000 0001 0100
	//1111 1111 1111 1111 1111 1111 1110 1011
	//1111 1111 1111 1111 1111 1111 1110 1100
	unsigned int j = 10;
	//0000 0000 0000 0000 0000 0000 0000 1010
	printf("%d\n", i + j);
	//按照补码的形式进行运算,最后格式化成为有符号整数
	//1111 1111 1111 1111 1111 1111 1111 0110
	//1111 1111 1111 1111 1111 1111 1111 0101
	//1000 0000 0000 0000 0000 0000 0000 1010
	//-10

	return 0;
}

//题目5:
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		//0000 0000 0000 0000 0000 0000 0000 1001
		//.....
		//0000 0000 0000 0000 0000 0000 0000 0001
		//0000 0000 0000 0000 0000 0000 0000 0000
		//1111 1111 1111 1111 1111 1111 1111 1110
		printf("%u\n", i);
		//9 8 ...... 1 0 4294967295 死循环
	}

	return 0;
}

//题目6:
#include <string.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d\n", strlen(a));
	//255
	//'\0'的ASCII码值为0
	//char类型数据的范围为-128~127
	//当上述循环的值抵达-128
	//再进行减法时因为char类型变量的空间大小所限
	//其值会变为127,接着逐次减1,最后到0
	//当strlen()函数计算时,遇到0结束计数

	return 0;
}

//题目7:
unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	//死循环打印hello world
	//当变量i 加至127 时,因为i为无符号的char
	//再加1,i会变为-128,如此循环

	return 0;
}

在这里插入图片描述

3. 浮点型数据

在内存中的存储方式

补充:大E计数法
1.03 × 1 0 8 10^8 108 记为 1.03E + 8

3.1 引子:数据以不同类型方式的存放与使用

int main()
{
	int i = 9;
	float* pi = (float*)&i;
	//以浮点数的方式访问整形数据
	printf("%f\n", *pi);
	*pi = 9.0;
	printf("%f\n", *pi);

	return 0;
}

在这里插入图片描述

由上述示例可见,浮点数与整形数据的区别不仅仅为精度的不同,更在于在内存中存储方式的不同。那么,浮点数究竟于内存中是如何存储的呢,我们紧接着来学习它。

3.2 浮点数存储规则

根据国际标准IEEE(电子和电子工程协会)754,任意一个二进制浮点数V可以写成以下的形式:
( − 1 ) S (-1)^S (1)S × M × 2 E 2^E 2E

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

示例:
十进制浮点数 5.0,它的二进制形式为101.0,以上述计数法来表示,则为 1.01 × 2 2 2^2 22
其中,S = 0,M = 1.01,E = 2

我们了解了以上的知识后,就可以正式学习浮点数在内存中的存储方法了。在内存中,浮点数并不是直接存储的,而是将以上计数形式中的三个信息S,M,E,分别存储与一块变量申请空间的不同部分,具体如下:

32位浮点数:(单精度浮点数)
在这里插入图片描述

64位浮点数: (双精度浮点数)
在这里插入图片描述
IEEE 754对于 有效数字M 与 指数E 还有一些特别的规定。
上文提到,M可以写成1.××××××的形式,其中×××××××的部分代表浮点数的小数部分。

  1. 在IEEE 754规定中,数据在向内存中存储时,因为第一位数总默认为1,所以会舍弃掉小数点前的1质保留其后的××××××部分。

示例:保存1.01时,就只会保存01,等到之后读取数据时再将第一位的1加上,这样做可以节省一位有小数字。当数据位32位浮点数时,留给有效数的位数只有23位,而当我们舍去第一位的1时,则就相当于我们保存了24位有效数位

  1. 关于指数E的规则就相较复杂,首先E位一个无符号整数,如此就以为这当 E为8位 时他的取值位0 ~ 255,而当其为 11位 时,他的取值就为0 ~ 2047。可是,在科学计数法中,指数位是可以取负值的,那么这一点应该如何解决呢。IEEE 754规定,在数据存入内存时,E的真实值必须要加上一个中间值当为8位时加127,当为11位时加1023

示例: 2 1 0 2^10 210,其 E为10,保存32为浮点数时,就应存入10 + 127 = 137。即1000 1001
注:127:0111 1111 1023:0011 1111 1111
除此之外,指数E从内存中取出时还可以分为三种情况:

  1. E不为全0或全1:对取出的数减去127,得到真实值,然后将有效数字1加至真实值前

示例:0.5,其二进制形式为0.1,由于规定中整数部分必须为1,我们将小数点向右挪移1位,则此数可以表达为1.0 × 2 − 1 2^-1 21,其阶码为 -1 + 127 = 126,表示为0111 1110,尾数去掉1后为0,补齐至23位,最终此数在内存中的存储的方式为:
0 01111110 00000000000000000000000

  1. E为全0:我们已知E存储时有中间值,分别在32位与64位时,其为127与1023,根据上述的存储规则可知,当内存中的E为全0时,其值为 M − 127 M^-127 M127大小无限接近于0。因此在规定中,我们直接将存储中E为全0的值规定为 1 − 127 1-127 1127(32位) 1 − 1023 1-1023 11023(64位)。这样规定,是为了表示 + − 0 +-0 +0,以及接近于0很小的数字,此时有效数字M第一位不在为1,而是为0
  2. E为全1:当内存中E的区域为全1时,如果此时有效数字M为全0表示 + − 无穷大 +-无穷大 +无穷大

引子题解:

  1. 以整形方式于内存中存储数字9,当以浮点型数据的方式访问时,为何得到的数据为0.000000.
    具体如下,数字9,在内存中的存储形式为 0000 0000 0000 0000 0000 0000 1001
    当我们以浮点数方式读取此段数据时,我们先将数据分区,符号S为0,即正,指数E为00000000,数值位为00000000000000000001001
    V = ( − 1 ) 0 (-1)^0 (1)0 × 0000000000000000000100 1 − 127 00000000000000000001001^-127 00000000000000000001001127
    可见此数为一个数值非常小无线接近于0的正数,十进制小数表示为0.000000
  2. 浮点数9.0,如何用二进制表示,还原成十进制后是多少
    9.0 转换为二进制形式为 1001.0,转换为科学计数法表达式为1.001 × 2 3 2^3 23
    由此可知,S = 0,E = 3 + 127,M = 001(有效数位)
    所以浮点数9.0在内存中的存储形式为
    0 10000010 001 0000 0000 0000 0000
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值