【C语言】数据存储

1.整型的存储

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
例如:
int a=20;
int b=10;

分别为a和b开辟4个字节大小的空间,那在计算机内存中如何对他们进行存储?

计算机中的整数有三种表示方法,即原码、反码和补码

三种表示方法均有 符号位 数值位 两部分,符号位都是用 0 表示 ,用 1 表示 ,而 负整数的原反补码的表示方法有所不同。
对于负数:
原码
直接将某个正负数按照二进制的形式翻译就可以。
反码
将原码的符号位不变,其他位依次按位取反就可以得到了。
补码

反码+1

对于正数:

正数的原、反、补码都相同,求得原码即可求的反和补码;
对于整形来说:数据存放内存中其实存放的是补码。
原因是因为: 使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器 )计算机不做减法。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

 上图是基于VS2019,展示整型数在内存中的存储。可以看到,内存中是以16进制补码的方式进行的存储。但是,在存储方式有些不同。这涉及到大小端存储的问题。

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址
中;小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位, ,保存在内存的高地址中。通常情况下,可认为逆序存储为小端字节序,而正序存储为大端字节序。例如:

例题 1:

#include <stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}
//char型的取值范围是-128~127;
//所以而整数a=10000000 00000000 00000000 10000000 原码
//11111111 11111111 11111111 01111111  反码
//11111111 11111111 11111111 10000000  补码
//将整数a存储在char型中,要发生截断,即
//1000000 -a
//%u表示无符号整型,而从char型到整型需要进行整型提升
//整型提升按照该数本身是有还是无符号进行提升,该数有符号数,则提升符号位,该数无符号,提升0
//10000000是有符号数,第一位是符号位,所以提升为11111111 11111111 11111111 10000000
//将该数按照无符号整型对待,所以,直接将其当成原码进行打印;
//该二进制数转化为10进制后,结果为4294967168

 例2:

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}
//char型的取值范围是-128~127;char本身表示不了128,就算放进去,也不会是128;
//所以而整数a=00000000 00000000 00000000 10000000 原码、反码和补码相同
//将整数a存储在char型中,要发生截断,即
//1000000 -a
//%u表示无符号整型,而从char型到整型需要进行整型提升
//整型提升按照该数本身是有还是无符号进行提升,该数有符号数,则提升符号位,该数无符号,提升0
//10000000是有符号数,第一位是符号位,所以提升为11111111 11111111 11111111 10000000
//将该数按照无符号整型对待,所以,直接将其当成原码进行打印;
//该二进制数转化为10进制后,结果为4294967168

2.浮点型数据的存储

根据国际标准 IEEE (电气和电子工程协会) 754 ,任意一个二进制浮点数 V可以表示成下面的形式: (-1)^S * M * 2^E
其中,(-1)^S相当于是符号位,当浮点数为正数,s=0;反之,S=1;
M相当于有效数字,M>=1&&M<2;
E为指数位,相当于E表示移动了几位,向左移动为正数,向右为负数,例如:
十进制的5.5,写成二进制是 101.1 ,相当于 (-1)^0 * 1.011 * 2^2 
那么,按照上面的格式,可以得出s = 0,M = 1.011,E = 2
十进制的 - 5.0,写成二进制是 - 101.0 ,相当于(-1)^1 * 1.01 * 2 ^ 2 ,那么s = 1,M = 1.01,E = 2
0.5(1 / 2)的二进制形式为0.1,由于规定有效数部分必须为大于等于1的数,即将小数点右移1位,则为(-1)^0 * 1.0 * 2 ^ (-1),那么s=0,M=1.0,E=(-1);
对于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 位有效数字。
至于指数 E ,情况就比较复杂。
首先, E 为一个无符号整数( unsigned int
这意味着,如果 E 8 位,它的取值范围为 0~255 ;如果 E 11 位,它的取值范围为 0~2047 。但是,科学计数E 是可以出现负数的,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,确保其一定会成为无符号正整数,对于 8bite 位的 E,这个中间数 127 ;对于 11 位的 E ,这个中间数是1023 。比如, 2^10 E 10 ,所以保存成 32 位浮点数即E为8位时,必须保存成 10+127=137,即10001001
1. E 不全为 0 或不全为 1
0.5 1/2 )的二进制形式为 0.1 ,由于规定正数部分必须为 1 ,即将小数点右移 1 位,则为
(-1)^0 * 1.0 * 2^(-1) ,E为 -1+127=126 ,表示为01111110,而尾数 1.0 去掉整数部分为 0 ,补齐 0 23 00000000000000000000000 ,用它表示M位。所以,最终0.5的二进制表示形式为:
0 01111110 00000000000000000000000
2.E 全为 0
这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值,有效数字M 不再加上第一位的 1 ,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于
0 的很小的数字。

举例:

int main()
{
 int n = 9;
 float *pFloat = (float *)&n;
 printf("n的值为:%d\n",n);//9
 printf("*pFloat的值为:%f\n",*pFloat);//0.000000
 *pFloat = 9.0;
 printf("num的值为:%d\n",n);//1091567616
 printf("*pFloat的值为:%f\n",*pFloat);//9.000000
 return 0; 
}

首先,求%d,可根据题目直接写出n=9;而第二问中,求将&n强制类型转换成float型后,用浮点数表示。首先,int n=9的二进制表示为:0000 0000 0000 0000 0000 0000 0000 1001,则在强制类型转换后将该二进制直接认为是浮点形式的存储,S=0,E=00000000,M=00000000000000000001001,所以此时的浮点形式表示为(-1)^0*0.00000000000000000001001×2^(-126),用十进制小数表示就是0.000000。

第三问中,浮点数9.0的表示形式为1001.0,所以(-1)^0*1.001*2^3,所以,S=0,E=3+127=130,写成二进制为10000010,M=1.001。所以浮点数9.0整体写为二进制形式为:

0 10000010 001 0000 0000 0000 0000 0000,题目中要求该数的%d,所以将其当做整型看待,其整型数为1091567616 。

第四问,求浮点数的浮点表示,直接得出9.000000

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值