整型提升规则及其注意事项

    我们知道,整数在计算机中有三种表示方法,即原码、反码、补码。要了解整型提升规则,我们首先要了解原码、反码、补码的基本概念。

    原码、反码、补码均分为符号位数值位两部分,符号位为最高位,“1”表示负数,“0”表示正数。在有符号数中,正整数的原码、反码、补码相同;负整数的原码为十进制正数转换后的二进制数,且最高位为1,负整数的反码为原码的最高位不变,其他位取反,负整数的补码则是在此基础上加“1”。

    那么计算机中为什么要存在原码、反码、补码呢?可以举出一个例子加以理解,我们知道计算机的CPU是只有加法器的,那么计算机如何实现减法运算呢?我们看下面一段代码:

#include <stdio.h>
int main()
{
	int a = 20;
	int b = -10;
    int c = a + b;
	return 0;
}

    对于变量a、b,其所开辟的空间中存放的是数据“20”和“-10”的补码,“20”为正整数,所以其原码、反码、补码相同,即“0000 0000 0000 0000 0000 0000 0001 0100”,“-10”的原码为:“1000 0000 0000 0000 0000 0000 0000 1010”,“-10”的反码为:“1111 1111 1111 1111 1111 1111 1111 0101 ”,“-10”的补码为:“1111 1111 1111 1111 1111 1111 1111 0110”。

    将“20”与“-10”的补码相加,为:“1 0000 0000 0000 0000 0000 0000 0000 1010”。由于int型数据所占内存大小只有4个字节,且一般PC存储数据模式为小端模式,即数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中,所以“1 0000 0000 0000 0000 0000 0000 0000 1010”中的最高位“1”被舍弃,留下数据“0000 0000 0000 0000 0000 0000 0000 1010”,即十进制“10”,这就是计算机进行减法运算的规则,通过这个例子我们也可以知道原码、反码、补码到底有什么用处,当然原码、反码、补码的作用还远不止于此。

    上面都是在介绍原码、反码、补码的基本概念,接下来进入正题,开始介绍整型提升的规则。首先来看一段代码:

#include <stdio.h>
int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("%d\n", c);
	return 0;
}

    我们知道,char类型也是int类型的一种,但char类型所开辟的空间只有一个字节大小,显然是无法存放int型的32位数据的,但是输出时却是“%d”的整型数据类型,所以在操作char类型数据的运算时,需要将数据进行整型提升,提升为int型的32位数据来进行计算。整型提升的规则是:先将十进制数据进行转换为八位的二进制数据,如“3”转换为:“0000 0011”,接着,再将八位数据“提升”到32位。提升规则为:若最高位为“0’,则补全数据全为0;若最高位为”1“,则补全数据全为1,则”3“、”127“整型提升后的补码分别为:“0000 0000 0000 0000 0000 0000 0000 0011”、“0000 0000 0000 0000 0000 0000 0111 1111”,由于3和127都是正整数,所以原码、反码、补码相同。将“3”和“127”的补码相加得“0000 0000 0000 0000 0000 0000 1000 0010”。将所得数据存入c变量,取“1000 0010”,所以c的补码为1000 0010,但这仅仅是c的补码,我们需要再次对c的补码进行整型提升,得到c的原码才能求出输出打印结果。

    通过前面所讲的规则,c的补码为:“1111 1111 1111 1111 1111 1111 1000 0010”,可知c的原码为:“1000 0000 0000 0000 0000 0000 0111 1110”,此时可知原码对应的打印结果为-126。

    这个只是大概说清楚了整型提升的基本概念以及原码、反码、补码之间的转换,但是对于初学者来说,还是很容易被“char”、“int”、“有符号数”和“无符号数”等概念弄得晕头转向,若要理清它们之间的关系,我们还需要多做练习。

练习1:

#include <stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);
	return 0;
}

     从这段代码来看,我们很容易知道a、b、c的补码均为“1111 1111”,但是在进行整型提升时,因为定义a、b、c的数据类型不一样,所以整型提升后的原码也不同,对于char和signed char来说,因为char类型默认为有符号型,所以整型提升后与signed char对应的原码相同,即“1000 0000 0000 0000 0000 0000 0000 0001”,打印结果为-1。

    对于unsigned char 来说,整型提升时补充数字全为零,所以c的补码为“0000 0000 0000 0000 0000 0000 1111 1111”,因为其为正数,所以补码反码原码相同,打印结果为255。

练习2:

#include <stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}

    首先显然可以看出-128为负数,所以其原码、反码、补码不相同。写出-128的原码,即“1000 0000 0000 0000 0000 0000 1000 0000”,再由原码推出其补码为“1111 1111 1111 1111 1111 1111 1000 0000”,最后得出内存“a”中的八位数据为“1000 0000”。这一步是必须,无论是对于signed char 还是 unsigned char一样。

    接着将“1000 0000”进行整型提升,得到补码为“1111 1111 1111 1111 1111 1111 1000 0000”(这个时候一定要注意,因为打印出的数据类型是无符号数,所以这时我们要把a作为正整数来看待,在将-128放入内存时是将它作为一个负数看待,在打印时又变成了一个正数,所以这里非常容易出错和混淆,需要特别注意)

    最后,打印结果为4294967168

练习3:

#include <stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
}

    通过上面的两个练习,可以总结出一套流程来分析此类问题,我将此类问题的分析步骤总结为三步:

1、判断从哪里取?

2、判断正负数。

3、判断取出来放哪?

    以此题为例:判断从哪里取?意思就是要看存储数据的空间是怎么定义的,是int 还是char int 或者是signed/unsigned char int,若是unsigned char直接补“0”,若是char或signed char看最高位是什么就补什么,int不需要补任何数据。

    判断正负数,这一步非常容易出错,有些数本来就是正数,有些数是由unsigned char类型定义的,以上两类不需要进行原码反码补码转换,对其进行转换后得出的结果肯定是错误的。

    判断取出来放哪?意思就是打印输出时,其类型是“%d”还是“%u”。若为“%u”,则不需要进行补码转换,直接将32位二进制转换成十进制就是输出结果;若为“%d”,则不但需要进行原码转换,还需要根据最高位来判断正负。

    所以可以改变练习3代码中的输出数据类型来验证,若将输出类型改为“%d”,结果为-10;若将输出类型改为“%u”,结果为4294967286。

整型提升时的注意事项:

1、在对“c”进行整型提升时,要先将“a+b”的补码进行截断,再进行整型提升。

2、若“c”的补码不是“1000 0010”,而是“1 0000 0010”,此时要注意char c的最高位是“0”而不是“1”。

3、在得到数据的补码后,首先要判断其反码补码原码是否一致,也就是说判断其是否为正数,不要一得到补码就进行转换,很容易出错。

4、在练习2中,将-128放入内存时,是将它作为负数看待的,所以这个时候要转换它的原码、反码、补码,但是将它从内存中读取并打印时,是将它作为一个无符号数看待的,这个时候若仍然进行原码反码补码转换,会出现错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马孔多镇长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值