C语言:数据的存储

目录

一、数据类型

1).整型

2).浮点型

3).自定义类型

4).空类型

二、整型在内存中的存储

1).原码、反码、补码

​编辑2).大小端内存存储顺序

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

1).存储

2).取出

1、E全为0(无穷小)

2、E全为1(无穷大)

3、E不全为0或不全为1(正常情况)

4、注

一、数据类型

名称                                                                    大小(字节)

char                           字符数据类型                      1

short                          短整型                                 2

int                              整型                                     4

long                           长整型                                 4

long long                  更长的整型                          8

float                           单精度浮点数                      4

double                       双精度浮点数                      8

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 ]

long  long

        unsigned  long  long  [ int ]

        signed  long  long  [ int ]

字符在存储的时候存储的的时ASCII码值,ASCII是整数,所以字符属于整型家族。且由编译器决定是signed  char还是unsigned  char

2).浮点型

float

double

3).自定义类型

数组类型

结构体类型  struct

枚举类型  enum

联合类型  union

4).空类型

void表示空类型

通常应用于函数的返回类型、函数指针、指针类型

二、整型在内存中的存储

1).原码、反码、补码

整数的2进制表示方法有三种,即原码、反码、补码

有符号的整数,三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示“负”,最高位的一位是被当做符号位,剩余的都是数值位

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

负整数的三种表示方法各不相同

原码:直接将数值按照正负数的形式翻译成二进制

反码:将原码的符号位不变,其他位依次按位取反

补码:反码+1

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

原因:

在计算机系统中,数值一律用补码来表示和存储

原因在于,使用补码,可以将符号位和数值域统一处理; 同时,加法和减法也可以统一处理(CPU只有加法器,即1-1=1+(-1))此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路

例如:b在内存中存放的数值为

int  b  =  -1;

10000000 00000000 00000000 00001010 —— 原码

11111111 11111111 11111111 11110101 —— 反码

11111111 11111111 11111111 11110110 —— 补码

由此可见,内存中存的是补码

原码到补码有1种转换方式,补码到原码有2种

2).大小端内存存储顺序

其实超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,分 为大端字节序存储小端字节序存储

数据不光需要存放到内存中,还要在使用时以相同方式拿出来,所以只有两种方便的方法

端(存储)模式

数据的位字节内容保存在内存的地址处,数据的位字节内容保存在内存的地址处

小端(存储)模式

数据的位字节内容保存在内存的地址处,数据的位字节内容保存在内存的地址处

VS2022(x86)的存储顺序为小端字节序存储

为什么有大小端?

在计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bi位,但是在C语言中除了8bit的 char 之外,还有16bit的 short 型,32bit的 long 型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式

常用的 X86 结构是小端模式,而KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

题目:设计一个程序来判断当前机器的字节序

int check_sys(int n)
{
	return *((char*)&n);
}

int main()
{
	int n = 1;
	//0x00 00 00 01
	//小端 01 00 00 00
	//大端 00 00 00 01
	int ret = check_sys(n);
	if (n == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

再看这样一段代码

int main()
{
	char a[1000];
	int i = 0;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d\n", strlen(a));
	return 0;
}

随着循环进行,-1- i 的结果为

-1,-2,-3,-4……-127,-128,127,126……3,2,1,0,-1,-2……

char类型占8个bit位,-128的存放的二进制为10000000,减1为011111111,即127

又因为strlen遇到' \0 '会停止,且' \0 '的ASCII码值为0,所以打印结果为255

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

当遇到5.5,3.14这样的的浮点数,该如何存储到内存中?

先来看这样一段代码

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;
}

打印结果为:

在上面的代码中,num 和 *pFloat 在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大?根本原因还是浮点型和整型在内存中存储方式的差异

1).存储

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

例如:

十进制的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

IEEE规定:
对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M 

对于64位的浮点数,最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

IEEE 754对有效数字M和指数E,还有一些特别规定

对于M:

前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。 IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。

比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。

以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

对于E:

首先,E为一个无符号整数(unsigned  int) 这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

2).取出

1、E全为0(无穷小)

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

2、E全为1(无穷大)

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

3、E不全为0或不全为1(正常情况)

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1(怎么放进去,怎么拿出来)

如:
float  f  =  5.5f ;

存储

二进制为101.1

即(-1)^ 0 * 1.011 * 2 ^ 2

则S=0,M=1.011,E=2

按照规定存入内存

S位:0

E位:2+127=129,即 10000001

M位:减去1,保留011,即01100000000000000000000(后面补零)

合并

0100  0000  1011  0000  0000  0000  0000  0000

即40  b0  00  00(十六进制)

取出

先看E位中的数值(不是E)为129,符合第三种情况,则将M位加1并取出

即1.01100000000000000000000 * 2 ^ 2

怎么放进去,怎么拿出来

4、注

大部分浮点数都无法用二进制精确表示,只有5.5,9.0这样简单的浮点数可以

例如3.14中0.14永远无法用二进制精确表示,并且M位最多只可以存放23(或52)位,所以很多浮点数在内存中只能用二进制粗略表示

最后,我们再看一遍这个代码

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;
}

int  n  =  9 ;

以整型的形式存储

00000000000000000000000000001001 ——> 存入内存

printf ( " n的值为:%d\n ", n ) ;
以整型的形式取出,所以打印9

printf ( "  *pFloat的值为:%f\n ",   *pFloat ) ;

浮点数的形式取出,

0  00000000  00000000000000000001001

即(-1)^0 * 0.00000000000000000001001 * 2^-126

E位中的数值为0,为第一种情况,无穷小,且为浮点数,所以打印0.000000

*pFloat  =  9.0 ;

以浮点型的形式存储

9.0的二进制为1001.0

(-1) ^ 0 * 1.001 * 2 ^ 3

S=0、E=3、M=1.001

S位:0

E位:3+127=130,即 1000001

M位:减去1,保留001,即00100000000000000000000(后面补零)

合并

0  10000010  00100000000000000000000

01000001000100000000000000000000 ——> 存入内存

printf ( " num的值为:%d\n " , n ) ;

以整型的形式取出,所以打印1091567616

printf ( "  *pFloat的值为:%f\n  ",   *pFloat ) ;

以浮点数的形式取出,怎么放怎么拿,所以打印9.000000

完,感谢阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值