深度剖析数据在内存中的存储

数据类型的介绍

前面的学习中,我们已经学习了基本的内置类型 :

char …………………………字符数据类型
short…………………………短整型
int……………………………整型
long long……………………长整型
float…………………………单精度浮点数
double………………………双精度浮点数

对于数据类型,我们还需要了解每个类型的所占储存空间的大小。在这里我们可以更深入的了解一些问题。
问题1:对于char类型来说,到底是字符还是整型,其实不需要纠结这个问题。我们来看这样一个例子:

#include <stdio.h>
int main()
{
	char a = 'J';
	printf("%d\n", a);
	return 0;
}

在这里插入图片描述
在这个例子中,我们可以看到对于char类型来说,我们将字符存储在a变量中,但是打印出来确实一个整型,所以对于char类型来说,既是字符类型,也是整型类型;同理,对于int和其他整型来说也可以是整型也是字符型。在简而言之,可以这么理解,整型有char,short,int,long,long long这些类型分别所占空间为1,2,4,4/8,8个字节。char的字符类型只是它的一个名字而已。
问题2:float类型和int类型,都是占4个字节,为什么分的这么细致呢?
对于这个问题,其实是编译器看问题的视角。对于编译器来说,定义int类型,就相当于告诉编译器这4个字节是整型,应该按整型的方式处理数据,float类型也是如此。

类型的基本归类

整型家族

char:unsigned char,signed char
short:unsigned short (int),signed short(int)
int:unsigned int,signed int
long:unsigned long(int),signed
long (int) long long:unsigned long long(int),signed long long (int)

对于 short、int、long、long long来说直接定义(int a),此时的整型就是表示有符号的整型,而对于char类型来说,直接定义(char c),并不一定就是有符号的字符类型,对于char来说,是取决于编译器的,有的是无符号,有的是有符号的。

浮点家族

float
double

什么时候需要用float,什么时候用double?其实区分没有那么严格,当我们精度不需要那么高时,float就足够了,而double是8个字节,精度会比float高很多,比如在使用圆周率时,只需要3.14就足够了,那么我们采用float会更好,足够我们使用,也不会造成空间上的浪费。
以上两种家族属于内置类型,C语言对于类型的分类还有构造类型,指针类型,空类型。

构造类型

构造类型是程序员自己创造的类型:

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

数组其实是自定义类型,那么是什么类型呢?举个例子:

#include <stdio.h>
int main()
{
	int a = 10;
	int arr[10] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(int[10]));
	return 0;
}

对于上面的数组来说,int [10]就是这个数组的类型,关于这个只需要了解就行,不需要过多的计较。
关于结构体和枚举我们在前面都介绍过了,就不再重复,联合体类型将在后续给大家讲解。

指针类型

指针类型也是独立于其他类型之外的。显而易见,对于指针而言,不仅需要告诉编译器指向对象是属于什么类型,还需要告诉编译器这是一个指针也就是我们的(*)。

char * pa
short * pb
int *pc
long *pd
……

空类型

关于空类型,我们只需要了解两点:

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

对于返回类型和指针类型我们都见过,函数的参数也可以是空类型我们来看一个例子:

void test(void)
{
	printf("hehe\n");
}

这是void函数,对于传参来说void就限制了传参,不允许参数传到test()函数中去,否则会报警告。

整型在内存中的存储

原码、反码、补码

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。那接下来我们谈谈数据在所开辟内存中到底是如何存储的?
关于整型在内存中的存储我们在前面已经介绍过了。我们学习整型在内存中的存储需要了解原反补的概念。这次我们就系统的讲解。

计算机中的有符号数有三种表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同。

  • 原码
    直接将二进制按照正负数的形式翻译成二进制就可以。
  • 反码
    将原码的符号位不变,其他位依次按位取反就可以得到了。
  • 补码
    反码+1就得到补码。
    (符号位是最高位;其他位为数值位,也叫有效位)
    对于整型来说,存入计算机中的所有数据都是补码形式,而对于正数来说,原码反码补码都相同,所以我们直接按照原码来存入读取就可以了,而负数需要通过计算由原码得到补码。计算方法就是原反补的定义。我们举个例子:
int a = 1;
int b = -1;

a和b在内存中是如何存储的呢?a因为是正数,所以原反补是相同的,所以原码就可以表示补码,00000000000000000000000000000001在这32位中最前面的“0”是符号位,表示的是正数,后面的31位表示的是数值;b是负数,所以通过计算得到补码,那么原码10000000000000000000000000000001最前面的“1”表示是负数,反码11111111111111111111111111111110(符号位不变,其他位取反),补码11111111111111111111111111111111(反码+1)所以-1在内存中存储就是32个“1”。

大小端存储

整型在内存是按补码形式,那么怎么放入内存呢?有两种方式,分别为大端和小端存储。

  1. 小端字节序存储
    把一个数字的低位字节的内容,存放在内存的低地址处。把高位字节的内容存放在内存的高地址处。
  2. 大端字节序存储
    把一个数字的低位字节的内容,存放在内存的高地址处。把高位字节的内容存放在内存的低地址处。
    如何理解呢?举个例子:
int a = 0x11223344;

我们定义一个a变量,十六进制为11223344(0x表示16进制)。
小端存储:在这里插入图片描述
大端存储:
在这里插入图片描述

浮点在内存的存储

浮点数的存储,并不像整形存储的那样是通过原反补的形式进行存储的,但也是通过二进制,那么是怎样的一种形式呢?我们来看一个例子:

#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("num的值为:%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表示指数位。

既然所有的浮点数都能写成这种形式,那么我们就可以通过存储S,M,E的值来得到相应浮点数。那么S,M,E如何存入到内存中呢?
在这里插入图片描述

第1个比特位用来存储S的值,S只能取1或者0。0表示正数,1表示负数。
第2位到到第9位用来存储E的值(共8位),但是不是直接存储E的值什么意思,我们又是会遇到E为负数(0.5此时E为-1),但是规定E不能为负数,那么这是需要加上127,也就是说,真实值加上127存入E中(-1+127=126,存入E)。
第10位到第32位用来存储M的值(共23位),我们知道M取值在[1,2),因此我们可以将小数点前面的“1”省略,用剩下小数点后面的位来存储M。
我们举个例子:5.5
我们先将5.5写成(-1)S * M * 2E,S为1,M为1.011,E为2,因此得到5.5=(-1) 0×1.011×22。所以存入S为1,E存入为2+127=129,M存入为011。
用二进制则为:
S:0
E:10000001
M:01100000000000000000000
整个表示为:01000000101100000000000000000000
同理,double类型也是如此,只是由原来的32位变成了64位
在这里插入图片描述
以上说的是有关 浮点数的存入内存,那么如何取出来呢?当然是怎么存进内存,就如何取出来了。的确如此,但是也有一些特殊的情况:当E全为1,和当E全为0时,我们就需要注意。
E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值, 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。

E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);

既然了解了浮点数的存储和取出,那么在看我们那个例子是不是就可以理解了呢,大家可以尝试一下为什么打印会是那样的效果。好了,关于数据的存储分享就到这里,如果有错误的地方,欢迎大家指正,谢谢各位!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Solitudefire

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

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

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

打赏作者

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

抵扣说明:

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

余额充值