数据在内存中的存储


前言

本篇文章主要是讲解一些常见数据类型在内存中的存储方式。

 


一、C语言中有哪些类型的数据

  1. char          字符类型
  2. short         短整型
  3. int             整型
  4. long          长整型
  5. long long  更长的整型
  6. float          单精度浮点型
  7. double      双精度浮点型

而整型又分为有符号类型和无符号类型:

unsigned (无符号类型):当我们定义无符号型数据时,要使用unsigned来定义数据

例如:unsigned char  这时就算无符号的char类型数据

signed (有符号类型):当不是无符号数据时,就算不使用signed来定义,也是默认为有符号的

例如:char 、signed char    都是定义的有符号数据

不同类型的数据在内存中占据的大小不同,使用的范围也就不同。

二、数据在内存中的存储方式

1.整型的存储方式

有符号数在内存中有三种二进制存储的方式,分别是 原码、反码、补码。

例如,整型的10的二进制编码分别为:

  1.原码:1010

将十进制的数字转换为二进制的数据就是这个数的原码,而在计算机当中,整型类型的数据占据了4个字节的空间,每个字节有8个bit位,也就是有32个二进制位的大小,所以在计算机中10的原码是:

00000000 00000000 00000000 00001010

在二进制的最左边是高位,右边为地位。

在有符号数当中,二进制的最高位代表着符号位,如果为负数,符号位为1,为正数,符号位为1。

如,-10的原码就为:

10000000 00000000 00000000 00001010

  2.反码:

正数的反码就是其原码,负数的反码就是符号位不变,其他位按位取反

10的反码:

00000000 00000000 00000000 00001010

-10的反码

11111111 11111111 11111111 11110101

 3.补码

正数的补码也是和原码相同,负数的补码就是反码加1

-10的补码

11111111 11111111 11111111 11110110

在计算机当中,有符号整数都是按照补码的方式存储的,然后也是按照补码来用的。

那么了解了这些有什么用呢?

下面我们来看一段代码:

int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

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

 

它不会把数据的按照什么补码来读取,而是直接看做原码读取,就算你的最高位是1,它也是看成无符号的。当 i 等于0时,再 -- ,得到的会是-1的补码,也就是

11111111 11111111 11111111 11111111

而 i 又是一个无符号数,而且是按无符号数来打印,所以这时候的 i 一个就是2^32-1,肯定大于0,而且最后也会一直循环,所以会造成死循环。

2.浮点数的存储方式

浮点数和整数在内存中的存储方式虽然都是二进制存储,但差别还是很大的。

举例来说:

5.0的二进制为 101.0,相当于1.01*2^2.

那么它的s=0,M=1.01,E=2.

-5.0的二进制为-101.0,相当于1.01*2^2.

s=1,M=1.01,E=2.

32位的浮点数,最高位是符号位s,接着的8位是指数E,剩下的23位是有效数M.

64位的浮点数,最高位是符号位s,接着的11位是指数E,剩下的52位是有效数M.

关于有效数M和指数E还有一些特别的规定:

而M总是在1到2之间的数,所以在计算机内部保存M时,默认这个数的第一位总是1,因此可以舍去,只保留后面的小数部分,例如 1.01 只保留 0.1 ,等到读取的时候再把1加上去。

至于E,就比较复杂了。

首先,E是一个无符号整数,这就意味着E如果为8位的话,它的取值范围为0-255,11位的话其取值范围就是0-2047。而在科学计数法中,E是可以出现负数的,所以IEEE 754规定,存入内存的真实值必须加上一个中间数,对于8位的E,加上的是127,对于11位的E,加上的中间数是1023.

比如,2^10的E是10,那么保存为32位浮点数时,必须保存为10+127=137,即10001001。

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

E不全为0或不全为1时:

   这时,E就会减去127得到真实值,然后再在有效数的前面加上第一位的1.

如:

0.5的二进制形式为0.1,科学计数法表示为:1.0*2^(-1),其指数位E就表示为-1+127

为01111110,底数1.0去掉1,表示为0,补齐到23位,所以其二进制表示为:

0 01111110 00000000000000000000000

E全为0时:

这时候指数E就等于1-127(或者1-1023)即为真实值。

有效数M不在加上第一位的1,而是还原成0.xxxx的小树,这样是为了表示0或者解决0的很小的数。

如:

0 00000000 10000000000000000000000

这里E:1-127=-126,M=1.1,s=0,数就为 (-1)^0 *(1.1)*2^(-126)  这里就是一个非常小的数,在计算机上打印出来的话就是0.

E全为1时:

这个时候,E代表的值就是255-127,然后即使当M全部为0时,这个数也是一个非常大的数,可以表示为正负无穷大,正负取决于符号位s。

三、大小端字节序

什么是大小端字节序:

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

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

为什么会有大小端呢

   在计算机中,我们以字节为单位,每个地址单元都对应着一个字节,一个字节为8bit。但在C语言中没出来8bit的char外,还有16bit的short,32bit的int,另外,对于位数大于8Wie的处理器,由于寄存器宽度大于一个字节,那么必然存在一个如何将多个字节安排的问题,因此就产生了大小端存储模式。

例如:一个short类型的x,x的值为0x11223344,那么在小端字节序存储模式下,它在内存中的存放方式为:     44 33 22 11

                 低地址 ---->  高地址

而在大端字节序中,其存储方式就会发生改变:
                       11 22 33 44

                  低地址--->    高地址

如何判断计算机是使用的大端存储模式还是小端存储模式呢?

用以下代码就可以实现:

int main()
{
	int a = 1;
	char* p = (char*)&a;
	if (*p == 1)
		cout << "这是小端";
	else
		cout << "这是大端";
	return 0;
}

为什么这样就可以判断大小端呢?让我们看一下小端在内存的存储形式

 可以看到,a是整型,占了4个字节,如果我们把它强制类型转换为char,那么我们就可以取到它低地址的一个字节,然后a又为1,所以根据存储形式,它只能为 00 或者 01,然后我们就可以判断,强制转换后的值是否为1,如果为1,则是小端,否则就是大端了。

结尾

要想学好计算机,那么数据的存储方式这些是必须要了解的,当我们对这些原理了解清楚后,我们的水平才能够有更好的进步。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值