2个字节能存多少个16进制_第2篇:C/C++ 有符号和无符号数字的迷途

本文主要详细讲述了无符号的各种负面特性。很多中文书籍或文章没有专门详细地解析清楚这方面的内容,所以我这里专门开篇写一文,在写本文的时候,也从老外的相关资料做了不少的借鉴,并且利用bitset这个工具函数,从内存二进制数据的角度来解析无符号和带符号整数的相关特性。下面的各个示例已经运行过一片,做了自己充分的分析。本文可能还不太完善,我也会在以后不定时地更新。

常用整数的区间

2f020eaeb60010f4a261931194b1175f.png

有符号整数溢出

下面的示例代码,是用来展示什么叫数字溢出(Integer Overflow)

int 

我们值整数33333(如果用auto关键字声明,编译器会为该值需要32位的int存储该值,实际该值真正用到就17位,除了最高位0作为MSB判断正/负,其余15位是为了内存对齐填充之用),但是我们故意强制存储在16字节就会发生溢出,而溢出的部分是前16位的高位地址,因为我们声明的变量是short类型为2个字节,CPU只会截取指定的16位中的二进制数据,如下图

aa27a051f29c584fc85a42280b393586.png

同时对于unsigned short的声明类型,CPU截取的16位字节码会被认为是二进制补码形式,这里其实就是-32203的绝对值的补码的后16位的字节码,因此这种溢出的后果就输出了-32203。

上面的示例代码,如果我们声明为 unsigned short的话,就不存二进制的补码形式,在无符号整数中也不存在所谓的MSB位。因此可以正确解读为33333。

副作用2:无符号整数的环绕

int 

其实无符号整数没有溢出的说法,如果被赋值的整数超出该无符号整数可以表示的范围, 任何大于该类型可表示的最大数字的数字都只是“环绕(wraps around)

65535在2字节整数范围内,因此65535可以。
但是
65536超出了范围,绕回了值0,二进制形式:0000000000000000
65537超出了范围,绕回了值1,二进制形式:0000000000000001
65538超出了范围,绕回了值2,二进制形式:0000000000000010

如此类推....
当我们向无符号类型的整数赋一个负数,无符号整数的环绕会从该无符号整数的最大整数方向开始环绕

int 

x=0 时,二进制形式: 0000000000000000,这个在取值区间内,是正常的
x=-1时,二进制形式: 1111111111111111,会被环绕成65535
x=-2 时,二进制形式: 1111111111111110,会被环绕成65534

副作用3:无符号整数的减法问题

许多老资格的C/C++程序员都推荐在日常项目尽量避免使用无符号整数,上文已经罗列了无符号整数的副作用了,这里再看看此处的代码

int 

我们得知2-5是等于-3,但是-3是无法在无符号整数类型中正确表示的,那么为什么会输出424967293,我们按照上面的例子提供思路,你推理得到,由于-3无法正常表示的,环绕到int类型最大整数范围的顶部。

-1环绕到4,294,967,295
-2环绕到4,294,967,294
-3环绕到4,294,967,293

7e05f4e2951da0aca1b7d6f9285d118c.png
错误的输出

我们再进一步用

std::bitset<32>(-3);
std::bitset<32>(4294967293);

可以得知一个下图的32位的二进制码,该字节码能对于不同的数据类型能做不同解读

  • 对于signed int能够解读为-3的二进制补码
  • 对于unsigned int 能够解读为4294967293

00985d4f6e0ec6d8074d2303d2251993.png

副作用4:无符号整数和带符号整数混用

第二,混合有符号和无符号整数时,可能会导致意外行为。例如我们将无符号的整数作为函数的参数,C编译器是无法检测传入的整数是否符合特定的程序逻辑的,尤其是无符号和带符号的比较问题,可以参见如下例子,这个例子引用自stackflow网站,而且提问率也非常高。

例如执行如下函数,我们希望给函数传递一个无符号整数,当传入参数大于10才执行特定操作,否则就不执行该操作。

void 

我们向foo中传递任意一个负数,都能该程序不按我们的逻辑完全跑偏了,编译器在编译程序的时候也没有警告。 因此,混淆带符号和无符号对于程序设计来说通常是一个逻辑错误。那么问题的根源是什么呢?stackflow的答案我仍然不太满意,同学们可以自行查找该问题,而问题的本质仍然提到无符号整数的环绕行为。至于-10被环绕成size_t的某个无符号整数的最大值,请读者自行思考,我就不重复熬述了。

3aaa8fa7a37521ecee84711b30aaf7ba.png
程序没有按我们的意愿去执行

而解决该问题即非常简单,只要我们将size_t类型的参数变换成带符号的int/long等可以令程序正常运行了。

直到写本文用的是C++17,这个问题C ++程序员仍然无法回避,当你翻开C++标准库的头文件,大量地使用了类似size_t作为参数的类型和函数返回值。我们必须勇敢面对无符号整数的各种副作用。

从那么多的副作用示例中,我们得到结论就是在我们的应用代码中尽量避免使用无符号类型的数字类型,但有时候我们必须从将标准符的API中返回size_t类型转换为带符号的数字类型版本。

正确面对无符号类型的整数

毫不奇怪,在任何类型的转换期间都可能发生奇怪的事情,并且使用无符号到有符号整数也不例外。但是,这些类型的转换特别棘手,因为它们可能导致并非总是引人注意的错误,并最终会影响您的C ++代码,当您具有可与STL库互操作的C ++代码时,无符号整数很常见。实际上,例如您的程序是一个使用在std :: vector容器中获取商品计数,则vector的size方法将返回就是size_t的类型的值,这是一个无符号整数。

那么,从无符号到带符号转换的过程中,我们如何获得singed类型变量的最大值?

转换前检查整数限制

从无符号整数到有符号整数的转换。我们需要检查输入的无符号的字面量值是否超过当前有符号整数类型的最大上限。 若超出在这种情况下,我们将继续抛出C ++异常来,提醒用户目前处理的整数超出了当前带符号整数的最大整数。

嗯~~,C ++标准库中有一个名为std :: numeric_limits的标准组件。 这是一个类模板,可用于查询算术类型(包括int)的各种属性。 您将类型的名称作“ numeric_limits <T> :: max()”将返回类型T的最大值。这里的例子以将最大值存储在int类型的变量中,因此我们可以简单地调用numeric_limits <int>::max() ,如下示例

template 

除非你从事的是天文学类似的计算,我敢肯定从无符号的int /long到带符号的int/long的转换的有效数据区间
足以应付日常开发的方方面面

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值