(3)
#include <stdio.h> int main() { char a = 128; printf("%u\n",a); return 0; }
题目解析
#include <stdio.h>
int main()
{
char a = 128;
//128的原码为:00000000 00000000 00000000 10000000
//128的补码为:00000000 00000000 00000000 10000000
//存放到一个char类型,要发生截断
//a的补码为:10000000
//a又要以无符号的形式进行打印,而在打印的过程中,a是一个有符号的数,所以先发生整型提升,补符号位
//a在打印时候的补码为:11111111 11111111 11111111 10000000
//而a正好是以无符号的形式进行打印的,那么原码反码补码三码统一,直接打印即可
printf("%u\n", a);
return 0;
}
事实上,我们会发现(2)和(3)题的答案是一样的,这是为什么呢?其实,-128和128存储到char类型是一样的,因为char类型的范围是-128~127,unsigned char类型的范围是0~255。而如果我们画成图的话,那么就是下面的样子
画一个圆来表示则是
也就是127+1=-128,所以打印出来的结果当然一样了
(4)
#include<stdio.h> int main() { int i = -20; unsigned int j = 10; printf("%d\n", i + j); return 0; }
题目解析:
#include<stdio.h>
int main()
{
int i = -20;
//-20的原码为:10000000 00000000 00000000 00010100
//-20的反码为:11111111 11111111 11111111 11101011
//-20的补码为:11111111 11111111 11111111 11101100
//i的类型是int刚好符合,所以不用发生整型提升
unsigned int j = 10;
//10的原码为:00000000 00000000 00000000 00001010
//10是正数,三码统一,并且unsigned int类型也不需要发生整型提升,所以直接放入即可
printf("%d\n", i + j);
//i的补码为:11111111 11111111 11111111 11101100
//j的补码为:00000000 00000000 00000000 00001010
// 结果为: 11111111 11111111 11111111 11110110
//由于j为unsigned int 类型,所以要发生算术转换,这个加起来的结果也是unsigned int类型
//但是由于是以%d的形式进行打印的,所以还是要把这个数据看出有符号的数据来看待
//所以最终打印的结果的补码为:11111111 11111111 11111111 11110110
//所以最终打印的结果的反码为:11111111 11111111 11111111 11110101
//所以最终打印的结果的原码为:10000000 00000000 00000000 00001010
//也就是-10
return 0;
}
(5)
#include<stdio.h> int main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%u\n", i); } return 0; }
对于这段代码,我们就比较容易看出来了,因为i是一个无符号的数,他永远不可能小于0,所以判断条件恒成立,结果为死循环
(6)
#include<stdio.h> int main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); return 0; }
这段代码的目的就是为了找出\0字符,而我们也不难看出数组中存储的数据为:
a数组中存储的为 -1 -2 -3...........-128 127 ........2 1 0 -1 -2.........
所以结果为128+127=255
(7)
#include <stdio.h> unsigned char i = 0; int main() { for (i = 0; i <= 255; i++) { printf("hello world\n"); } return 0; }
显然i是一个恒大于0且恒小于等于255的数,所以条件恒成立,所以为死循环
三、浮点型在内存中的存储
常见的浮点型有3.14 1E10(1*10^10)等
浮点型家族有float、double、long double
浮点型的定义在float.h中可以查看,整型在limits.h中可以查看
要了解浮点数在内存中的存储我们先预测一下这段代码的运行结果
#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; }
运行结果为,这显然与我们所猜测的大为不同
这是为什么呢?实际上,是因为浮点数在内存中的存储与整型在内存中的存储不是一套规则。当他以整型在内存中放好数据以后,又想依靠浮点数的规则拿出来这些数据当然结果有所区别了
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式: (-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示有效数字,大于等于1,小于2。M是一个二进制数
2^E表示指数位。因为M是需要转换为2进制数的,所以2为底数。刚好可以挪动小数点
比如说一个浮点数5.5
他首先要转化为二进制数,结果为101.1
而他又可以写为(-1)^0 * 1.011 * 2^2
也即是S=0,M=1.011,E=2
也就是说,任何一个浮点数都可以对应出他的S、M、E
有了S、M、E我们就可以进行存储浮点数了
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.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是可以出现负数的,所以IEEE 754规定,存入内存时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(1/2)的二进制形式为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);
有了这些理解,我们在来看一个例子
float a = 5.5;
//5.5 ----->(-1)^0 * 1.011 * 2^2
//也就是S=0,M=1.011,E=2
//(需要注意的是存储S一位,E有八位,M有23位,并且E需要加127,M要省略第一个1)
//所以存储为0 10000001 01100000000000000000000
//也就是0100 0000 1011 0000 0000 0000 0000 0000
//也就是40 b0 00 00
然后我们也回过头来看一下我们的9.0
float a = 9.0;
//9.0---->(-1)^0 * 1.001 * 2^3
//S=0,M=1.001,E=3
//所以存储为:0 10000010 00100000000000000000000
//也就是0100 0001 0001 0000 0000 0000 0000 0000
//也即是41 10 00 00
我们在回过头来看我们一开始的代码,我们就会茅塞顿开了
#include<stdio.h>
int main()
{
int n = 9;
//n的原码、反码、补码:00000000 00000000 00000000 00001001
//以浮点数的视角则为:0 00000000 00000000000000000001001
//也就是S=0,E=-127,M=1.00000000000000000001001
//近似为0了,由于精度问题,输出结果就是0了
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
//9.0---->(-1)^0 * 1.001 * 2^3
//S=0,M=1.001,E=3
//所以存储为:0 10000010 00100000000000000000000
//也就是01000001000100000000000000000000
//十进制刚好就是:1091567616
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
总结
本站主要讲解了数据的基本类型、原码反码补码、大小端、整型和浮点型在内存中的存储
如果对你有帮助,不要忘记点赞加收藏哦!!!
想获得更多优质的博客,一定不要忘记关注我哦!!!