C/C++中有符号数隐式类型转换成无符号数需注意的问题

前言

隐式类型转换转换是一个挺基础的概念,即使对于初学者来说都不会陌生,一般情况下是指数据类型的转换是由编译系统自动进行的,不需要人工干预的类型转换方式。与之相对的是强制类型转换,在进行转换时必须使用强制类型转换运算符进行转换,这种也被称为显式转换。

举例

隐式转换

1
2
short sn = 999;
int n = sn;

显示转换

1
2
float f = 110.741f;
int n = (int)f;

这两种转换方式平时经常用到,不管是函数传参时进行转换,还是数学计算时进行强转,一直也没有发现有什么问题,直到昨天遇到了一个有符号数隐式转换成无符号数时,才发现这里也是一个知识盲点,当时脑瓜儿嗡嗡的,怎么连隐式类型转换也这么陌生了呢?

其实隐式类型转换一般发生在小类型转换成大类型时,有个常用的关系链 char -> short -> int -> long -> float -> double,当关系链条中出现无符号数字时,情况有些难以理解了(实际上是有符号数字的锅)。

问题

看一下这几行代码,如果你能准确说出程序的输出值,那么你已经掌握了这个知识点,后面的内容可以不用看了:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main()
{
char c = 128;
unsigned int n = c;
cout << n << endl;

return 0;
}

这段代码的输出值是 4294967168,发生了啥?也就是说老板给你发工资时,本来想发128块,但是发工资的函数参数是 unsigned int 类型的,结果就给你发了 4294967168,一下就实现了40多个小目标。

查找原因

针对上面的代码我们改写一下,把变量 c 换成无符号类型:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main()
{
unsigned char c = 128;
unsigned int n = c;
cout << n << endl;

return 0;
}

这次的输出值变成了 128, 符合我们的预期,回过头来再看看刚才出错的代码,区别就是变量c是否有符号,结果差了好几十亿。

这里导致结果差异的原因实际上是符号位引起的,如果是无符号数字,从小类型到大类型隐式类型转换的结果数字都不会变,但是如果是有符号的数字,在转换成大类型数字的时候就要考虑符号位了,就以第一段代码为例来解释这个现象。

char c = 128; 这一句实际上已经超出了变量 c 的范围,因为变量c是有符号数字,所以它的范围是-128~127,这里赋值成128,实际在内存中的bit排列是 10000000,而有符号数的第一位bit表示正负号,这里是1表示这是一个负数,计算机存储负数是以补码的形式存储的,那么把这个数据按位取反再加1,得到 1000000 还是原来的数字,好神奇哦!

不过这里就可以计算出 c 实际上代表-128,那么它在隐式类型转换成更大的有符号数字时,需要保证值不变,一个int的-128怎么表示呢?根据补码的定义应该是11111111 11111111 11111111 10000000,这个数字再转换成 unsigned int 就是前面提到的 4294967168 啦。

总结

  • 有符号数字在转换成范围更大的无符号数字时需要注意转换所得数值是否正确,失之毫厘差之千里。
  • 总结一个规律,有符号的整形数字在进行隐式类型转换时实际上是在数字的二进制表示前面补充符号位。

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

一个人不能做完所有的事情,但是所有人都可以做一些事情,怕什么真理无穷,进一寸有进有一寸的欢喜~

2020-12-8 00:04:05

Albert Shi wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客