数据在内存中的存储方式

发布于:2023-01-09 ⋅ 阅读:(441) ⋅ 点赞:(0)

目录

1.C语言数据类型介绍

2.整数的存储方式

3.浮点数的存储方式


一.C语言中的数据类型

  (1)整型家族(char , short ,int , long)

char
unsigned char
signed char
short
unsigned short [ int ]
signed short [ int ]
int
unsigned int
signed int
long
unsigned long [ int ]
signed long [ int ]
其中不同整型类型,能够代表整型的大小范围也不同,当然所占空间也不同,char占1个字节,short占2个字节,int占4个字节,long占4个字节(不确定,c语言标准规定sizeof(long)>=sizeof(int)即可)

 (2)浮点型家族(double , float)

float      单精度浮点型
double  双精度浮点型

 图来自菜鸟教程网站

 (3)构造类型

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

(4)指针类型(int* , void * , char * , float*)

(5)空类型(void)

1.函数返回为空 (比如一个简单的打印函数,就不需要返回值,不返回值的函数返回类型即为空)

2.函数参数为空,有些函数,不接受参数,则参数类型可以设置为空

3.指针类型为void *,该类型指针无法解引用,无法进行指针运算,但它可以根据我们的需求,转化为任意数据类型.

二.整数的存储方式

1.原码,反码,补码

(1)补码出现的原因:
  数字在计算机中是以二进制的方式进行存储,区分正数或者负数是看最高位,最高位为0,则为正数;最高位为1,则为负数

 假设我们用int类型(32个bit位)来存储1,-1,假设电脑中只存在原码

    1
    00000000 00000000 00000000 00000001
    -1
    10000000 00000000 00000000 00000001

由于CPU中只存在加法器,如果按照原码直接进行相加,得到的二进制,转为数字,得到的是-2,显然1-1 \neq -2,得到的答案是错误的.

于是聪明的科学家想了一种方法,也就是原码.反码.补码的方法

通过这种方法,我们不仅实现了符号位和数值域进行统一处理,无论负数还是正数,都有唯一一个二进制与之对应转化,同时,我们通过补码,统一了加法和减法运算.

 通过这种方式,我们就可以解释为什么有符号char类型,范围在-128到127之间

(2)具体实现 

比如-1,这里拿一个字节存储举例

它的1原码是1000 0001,符号位不变,所有位按位取反,则它的反码是1111 1110

反码+1得到补码,也就是1111 1111,所以-1在计算机中就是1111 1111来存储的.

当然补码得到原码的另一种方式,除了逆着进行外,还可以重新按照原码得到补码的方式再重新操作一遍,非常神奇!!!

2.大小端存储

 在了解正数以补码的形式存储后,我们就可以打开vs2019进行内存监视了,16的十六进制为10,在内存监视中,我们可以很好的观察到,不过为什么有点像是倒着存的?

数据的存储有如图两种存储方式, 统称为字节序存储(以字节为一个单位),像char类型一个字节大小,则完全没有大端小端这样的概念.

一种称为大端(字节序, 指数据的低位存储在高地址,数据的高位存储在内存的低地址

一种称为小端  (字节序),指数据的低位存储在低地址,数据的高位存储在内存的高地址

因此由上图在内存中的显示,我们可以知道,在目前X86环境底下是以小端字节序存储.而在我们知道的KEIL C51是大端字节序存储,有些ARM处理器,其可以通过硬件,根据自己的设置,调整是大端还是小端.

至于如何判断是大端还是小端,我们可以简单设计一个代码,1在大小端存储是不同的,我们取出1的地址(低地址),将其转为char *类型,再解引用(访问一个字节),得到1即为小端,得到0即为大端.

int check_sys(int x)
{
	return (*(char*)&x);
}
#include <stdio.h>
int main()
{
	int a = 1;
	if (check_sys(a))
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

三.浮点数的存储

1.浮点数的二进制转化

根据国际标准IEEE,任意一个二进制浮点数都可以表示为这样的形式(类似十进制的科学计数法)

  (-1)^S * M * 2^E

其中S用来确定正负,正数为0,负数为1,M是具体的一个数,范围在1~2间,E代表指数位

比如5.5,用二进制表示101.1,然后类似科学计数法,101.1可以表示成1.011*2^2,然后是正数,所以5.5即可表示为(-1)^0 * 1.011 * 2 ^ 2,S为0,M为1.011,E为2

但是我们也可以发现一些像是无限不循环小数,比如pi,只能无限逼近,而不能精确表示出来,所以我们也说C语言中,不能直接进行浮点数比较,原因就在此,本身存储就不是精确的.

2.存储方式

无论是单精度(float)或者是双精度 (double),其在内存中都是按照这样的格式进行存储,唯一不同的是float是32个比特位,而double是64个比特位

bit位数目  S E M

float

1 8 23
double 1 11 52

对于M,由于M的取值范围必定在1~2之间,所以可以省略前面的1,例如1.011就可以省略1,只存入011进去,这样就可以多保存一位,达到24位

对于E,E是一个unsigned int类型的数字,但我们指数是可能出现负数的,所以为了让负数变为正数(无符号整型),像float类型,我们便加一个127(8个bit位能表示的最大负数)再存进去,对于double类型,则加一个1023再存进去.

Lg.例如0.5  二进制标准形式为(-1)^0 * 1.0 * 2 ^ (-1)   (1) -1+127 = 126 (2)1.0的1可以去掉

其在内存中存储即为0 01111110 00000000000000000000000

当E全为0,它原本的指数即为-127(1000 0001 + 0111 1111),代表一个非常接近0的数字;

当E全为1,它原本的指数即为128(1000 0000 + 0111 1111), 并且有效数字也全为0,则代表无穷大,由S区分,正无穷还是负无穷.


网站公告

今日签到

点亮在社区的每一天
去签到