C语言存储

C语言:数据在内存中的存储
13/100
发布文章
z3256707200
未选择文件

整数在内存中的存储

整数的二进制表示方法有三种:原码,反码,补码

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

正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。

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

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

大小端字节序和字节序判断

当我们了解了整数在内存中存储后,我们调试看一个细节:

#include <stdio.h>
 int main()
 {
 	int a = 0x11223344;
 	return 0;
 }

在这里插入图片描述
调试的时候,我们可以看到在a中的0x11223344这个数字是按照字节为单位,倒着存储的。这就是由于字节在内存中的存储决定的。

什么是大小端?

超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分
为大端字节序存储和小端字节序存储,下面是具体的概念:

大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。
小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的高位字节内容,保存在内存的高地址处。

为什么要有大小端

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

设计程序判断机器的大小端

//方法一:使用指针强制类型转换
#include <stdio.h>
 int check_sys()
 {
 	int i = 1;
 	return (*(char *)&i);
 }
 
 int main()
 {
 	int ret = check_sys();
 	if(ret == 1)
 		printf("小端\n");
 	if(ret == 0)
 		printf("大端\n");
 	return 0;
 }
 //方法二:使用联合体
 int check_sys()
 {
 	union
 	{
 		int i;
 		char c;
 	}un;
 	un.i = 1;
 	return un.c;
 }

浮点数在内存中的存储

常见的浮点数:3.14159、1E10等,浮点数家族包括float,double,long double类型。

先看代码:

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", * pFloat);

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

	return 0;
}

输出结果如下:
在这里插入图片描述
上述代码中,n*pFloat在内存中存的是相同的内容,但是从浮点数和整数的角度解读结果大相径庭。

要理解这个结果,就要搞懂浮点数在计算机内部的表示方法。

根据国际标准IEEE(电器和电子工程协会)754,任意一个而进制浮点数V可以表示成下面的形式:
在这里插入图片描述
举例来说:
十进制的5.0,写成二进制是101.0,相当于1.01×2^2。那么,按照上面V的格式,可以得出S=0,M=1.01,E=2。十进制的-5.0,写成二进制是-101.0,相当于-1.01×2^2。那么,按照上面V的格式,可以得出S=1,M=1.01,E=2。

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

浮点数的存储过程

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

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

⾄于指数E,情况就比较复杂

首先,E为一个无符号整数(unsignedint)

这意味着,如果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。

浮点数的读取过程

指数E从内存中取出还可以再分成三种情况:

E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为: 0 01111110 00000000000000000000000

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

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

题目解析

现在,我们再看一开始给的代码。

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", * pFloat);

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

	return 0;
}

9以整型的形式存储在内存中,二进制序列为:00000000 00000000 00000000 00001001

float*类型的指针读取到此地址,会认为:符号位S=0,后面的八位指数E=00000000,最后23位的有效数字M=00000000000000000001001

由于指数E全为0,所以符合E为全0的情况。因此,浮点数V就写成:
V=(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146)
显然,V是⼀个很小的接近于0的正数,所以用十进制小数表示就是0.000000。

再看第2环节,浮点数9.0,为什么整数打印是1091567616.

⾸先,浮点数9.0等于⼆进制的1001.0,即换算成科学计数法是:1.001×2^3,所以:
9.0 = (−1)^0 ∗ (1.001) ∗ 2^3,那么,第一位的符号位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
文章目录
整数在内存中的存储
大小端字节序和字节序判断
什么是大小端?
为什么要有大小端
设计程序判断机器的大小端
浮点数在内存中的存储
浮点数的存储过程
浮点数的读取过程
题目解析
整数在内存中的存储
整数的二进制表示方法有三种:原码,反码,补码。

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

正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。

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

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

大小端字节序和字节序判断
当我们了解了整数在内存中存储后,我们调试看一个细节:

#include <stdio.h>
int main()
{
int a = 0x11223344;
return 0;
}
在这里插入图片描述
调试的时候,我们可以看到在a中的0x11223344这个数字是按照字节为单位,倒着存储的。这就是由于字节在内存中的存储决定的。

什么是大小端?
超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分
为大端字节序存储和小端字节序存储,下面是具体的概念:

大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。
小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的高位字节内容,保存在内存的高地址处。

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

设计程序判断机器的大小端
//方法一:使用指针强制类型转换
#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);
}

int main()
{
int ret = check_sys();
if(ret == 1)
printf(“小端\n”);
if(ret == 0)
printf(“大端\n”);
return 0;
}
//方法二:使用联合体
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
浮点数在内存中的存储
常见的浮点数:3.14159、1E10等,浮点数家族包括float,double,long double类型。

先看代码:

int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf(“n的值为:%d\n”, n);
printf(“*pFloat的值为:%f\n”, * pFloat);

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

return 0;

}
输出结果如下:
在这里插入图片描述
上述代码中,n和*pFloat在内存中存的是相同的内容,但是从浮点数和整数的角度解读结果大相径庭。

要理解这个结果,就要搞懂浮点数在计算机内部的表示方法。

根据国际标准IEEE(电器和电子工程协会)754,任意一个而进制浮点数V可以表示成下面的形式:
在这里插入图片描述
举例来说:
十进制的5.0,写成二进制是101.0,相当于1.01×22。那么,按照上面V的格式,可以得出S=0,M=1.01,E=2。十进制的-5.0,写成二进制是-101.0,相当于-1.01×22。那么,按照上面V的格式,可以得出S=1,M=1.01,E=2。

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

浮点数的存储过程
IEEE 754对有效数字M和指数E,还有一些特别规定。

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

⾄于指数E,情况就比较复杂

首先,E为一个无符号整数(unsignedint)

这意味着,如果E为8位,它的取值范围为0255;如果E为11位,它的取值范围为02047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

浮点数的读取过程
指数E从内存中取出还可以再分成三种情况:

E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为: 0 01111110 00000000000000000000000

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

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

题目解析
现在,我们再看一开始给的代码。

int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf(“n的值为:%d\n”, n);
printf(“*pFloat的值为:%f\n”, * pFloat);

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

return 0;

}
9以整型的形式存储在内存中,二进制序列为:00000000 00000000 00000000 00001001

float*类型的指针读取到此地址,会认为:符号位S=0,后面的八位指数E=00000000,最后23位的有效数字M=00000000000000000001001。

由于指数E全为0,所以符合E为全0的情况。因此,浮点数V就写成:
V=(-1)^0 × 0.00000000000000000001001×2(-126)=1.001×2(-146)
显然,V是⼀个很小的接近于0的正数,所以用十进制小数表示就是0.000000。

再看第2环节,浮点数9.0,为什么整数打印是1091567616.

⾸先,浮点数9.0等于⼆进制的1001.0,即换算成科学计数法是:1.001×2^3,所以:
9.0 = (−1)^0 ∗ (1.001) ∗ 2^3,那么,第一位的符号位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。

语法说明
标题文本样式列表图片链接目录代码片表格注脚注释自定义列表LaTeX 数学公式插入甘特图插入UML图插入Mermaid流程图插入Flowchart流程图插入类图快捷键
标题复制

一级标题

二级标题

三级标题

四级标题
五级标题
六级标题

Markdown 3634 字数 159 行数 当前行 1, 当前列 0HTML 3290 字数 115 段落
发布博文获得大额流量券

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值