数据在内存中的存储

1、数据类型介绍

char 字符数据类型 一个字节
short 短整型 两个字节
int 整型 四个字节
long 长整型 在32位环境占据4个字节,64位环境占据8个字节
long long 更长的整型 八个字节
float 单精度浮点型 四个字节
double 双精度浮点型 八个字节

类型的意义:

  1. 内存里面存的是什么
  2. 怎么解读这块内存

1.1 基本类型的归类

整型家族

char
  unsigned char
  signed char
short
  unsigned short [int]
  signed short [int]
int
  unsigned int
  signed int
long
  unsigned long [int]
  signed long [int]

浮点数家族

float
double

构造类型----自定义类型,可以自己创建出新的类型

数组类型
  数组类型去掉他的数组名就是他的类型,如 int arr [10]; int arr [5]; char arr [10]; 的类型分别
  是 int [10]; int [5]; char [10];
结构体类型 struct
  struct student1; struct student2; 是定义的两个不同的结构体类型
枚举类型 enum
联合类型 union

指针类型

int* pi
char* pc
float* pf
void* pv  对于这种类型的指针变量不能直接解引用,也不能直接进行加
     减,必须先强制类型转换为其他类型才能进行解引用与加减运算。

空类型

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

2、整型在内存中的存储

2.1 原码、反码、补码

计算机中的整数有三种2进制表示方法,即原码、反码、补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”。最高位为符号位。

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

负数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码

对于二进制的原码来说,符号位仅仅表示正负,不过对于二进制的补码来说,最高位既是符号位也是实际的数(权值)。

如 -8 的二进制位为
10000000000000000000000000001000 原码
11111111111111111111111111110111 反码
11111111111111111111111111111000 补码
对于原码进行计算:-(1* 23)=-8
对于补码进行计算:-1* 231+ 1* 230+1* 229……1* 23+1* 23=-8
其实对于补码的计算并不用从最高位计算,补码往前+1并不会改变补码的数值。直接从1000计算即可。补码 1000计算,-1* 23=-8。四个比特位的取值范围是-8~7。-8在四个比特位所能包含的范围中。所以可以通过四个比特位计算。

  1. 一个数通过补码计算它的十进制数,为了便于口头计算,不用每次都从最高位32位开始计算,只要在一个相对能包含他的十进制位大小的比特位数开始计算即可。
  2. 补码最高位的权值与后面的权值相加不变.
    如+5的五位二进制位补码改写成八位二进制位补码,直接往前加0。00101----->00000101
    如-5的五位二进制位补码改写成八位二进制位补码,直接往前加1。11011----->11111011。
  3. 二进制位补码所能表示十进制位的范围:-2N-1 ~ 2N-1-1。如四位二进制所能表示的范围是-8 ~ 7。
  4. 两个补码进行运算时,注意编码的取值范围。
    如:用二进制位运算求出 13+10 13-10 -13+10 -13-10;应该选用几位几位二进制来进行运算?
    最好选用六位二进制位计算,因为六位二进制的补码的取值范围是-32~31。计算结果不会产生逸出。

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

int main()
{
	int a = 20;
	int b = -10;
	return 0;
}

a的存储
在这里插入图片描述
b的存储
在这里插入图片描述
20的补码为00000000000000000000000000010100,为了便于在屏幕上展示,采取了十六进制。四位二进制位为一位十六进值位。所以他的十六进制数为0x00 00 00 14。如屏幕所示,证实了计算机采取的是二进制补码进行存储的
-10的补码为11111111111111111111111111110110。它的十六进制是0xff ff ff f6。如屏幕所示,证实了计算机采取的是二进制补码进行存储的。
为什么采取二进制补码进行存储

  1. 只有补码在进行运算的时候才能将数值位与符号位进行同一处理。
    如 5+(-5)=0;用原码计算00000101+10000101=10001010,并不等于0,用补码进行计算00000101+11111011=0;
  2. 同时,加法和减法也可以同一处理(CPU只有加法器)。此外,补码与原码相互转换,其运算过程是相同的,即(原码=补码数值取反+1) 与 (补码=原码数值位取反+1)的逻辑相同。不需要额外的硬件电路。
  3. 我们可以看到对于a和b分别储存的是补码。但是我们发现顺序有点不对劲。为什么?
    数据的存储有大小端之分。

2.2 大小端介绍

大端存储模式:是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中;
小端存储模式:是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中;
假设一个十六机制数0x11223344,他的大小端存储分别如下:
在这里插入图片描述

根据2.1节内存图示,20的存储是14000000,-10的存储是f6ff ff ff,低位放在低地址处,所以存储方式是小端存储。大小端存储方式取决于硬件,不取决于编译器。

设计一个小程序来判断当前机器的字节序。

int check_sys()
{
	int a = 1;
	char* b = (char*)&a;
	return *b;
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

打印结果
在这里插入图片描述

3、浮点型在内存中的存储

浮点数家族

float
double
long double

3.1 一个例子

#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("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

打印结果
在这里插入图片描述

  1. 第一个与第四个打印结果比较好理解,以整型的方式存储,再以整型的方式打印,以浮点数的方式存储,再以浮点数的的方式打印。
  2. 第二 三的打印结果为什么会这样呢?

3.2 浮点数存储规则

3.1节的例子中,num与*pFloat在内存中明明是一个数,为什么浮点数和整数的解读结果会差别这么大?下面介绍浮点数在计算机内部的表示方法。

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

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

十进制:V=5.0f,------>二进制101.0------>科学计数法1.01 × 22=(-1)0 × 1.01 × 22
所以根据 V = (-1)S × M × 2E,S=0, M=1.01, E=2
十进制:V= -5.0f,----->二进制-101.0----->科学计数法-1.01 × 22=(-1)1× 1.01 × 22
所以根据 V = (-1)S × M × 2E,S=1, M=1.01, E=2
对于浮点数来说,内存中存储的就是S M E 这三个数,以此来表示浮点数。

对于32位的浮点数,最高的一位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
在这里插入图片描述

对于64位的浮点数,最高的一位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
在这里插入图片描述

浮点数存入时

对于有效数字M

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

对于指数E

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

浮点数从内存中取出时,指数E分为三种情况。

一、E不全为0或不全为1
这是,浮点数是怎么存的,就怎么取。即指数E的计算值减去127(或者1023),得到真实值,再将有效数字M加上第一位的1。
比如:
V=0.5f的二进制形式为0.1,由于规定整数部分必须为1,即将小数点右移1位,则为1.0 × 2(-1),所以E=-1+127=126,表示为01111110 (E写成二进制时,最高位不是符号位)。有效数字去掉整数部分,小数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为0 01111110 00000000000000000000000。给定一个二进制,反向操作就能写出其浮点数。
二、E全为0
00000000 ,还原为十进制为0,这时,浮点数指数E的真实值等于1-127(或者1-1023)。有效数字M不再加上第一位的1,而是还原为0.xxxxxxx的小数。因为1.xxxxxxx × 2(-127) ≈ 0,说明这个浮点数本身就是一个非常小的数;
为什么E等于1-127,因为1.xxxxxxx × 2(-127)=1.xxxxxxx / 2(127),当有效数字不再加上第一位的 1 时,相当于将有效数字左移了一位,分母左移一位,分子也得左移一位,所以1.xxxxxxx / 2(127)=0.xxxxxxx / 2(126),这就是为什么有效数字不加 1 ,E等于 -126或者 -1022。
三、E全为1
这时,如果数字M全为0,表示±∞大(正负位取决于符号位S)。因为对于32位的浮点数来说,E的计算值等于255(八位二进制的最大数是255),E的真实值等于255-127=128,V=(-1)S × 1.xxxxxxx × 2(128),是一个非常大的数,近似于无穷。

对于3.1节中的例子。

第一部分,以整数 9 写入,再以浮点数打印
9的二进制补码为---->00000000000000000000000000001001
当它以浮点数解读时,符合指数E全为0的情况,V=(-1)0×0.00000000000000000001001×2(-126)=1.001×2(-146),显然,V是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。
第二部分,当以浮点数 9.0 写入时,再以整数打印。
我们先看浮点数 V=9.0f 怎么写入的
9.0----->1001.0----->(-1)0× 1.001 × 23----->S=2, M=1.001,E=3+127=130
那么,第一位的符号位S=0,有效数字M等于001,后面补齐20个0,凑满23位,指数E等于3+127=130,即10000010,所以写成二进制形式,应该时S+E+M,即0 10000010 001 0000 0000 0000 0000 0000,这个32位的二进制数以整数的形式输出,还原为十进制,正是1091567616

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值