大小应该使用有符号整数还是无符号整数?
Posted
技术标签:
【中文标题】大小应该使用有符号整数还是无符号整数?【英文标题】:Should signed or unsigned integers be used for sizes? 【发布时间】:2017-11-14 10:33:37 【问题描述】:标准库vector::size()
给出一个size_t
,一个无符号数。在 CppCon 的一次演讲中,我听到有人(是 Chandler Carruth 吗?)说这很不幸,应该使用有符号整数。
背景是没有为有符号整数定义溢出,因此编译器有更多的余地。在一次谈话中,Carruth 展示了 uint8_t
作为 bzip2 中的 for
循环索引如何在 x86 上创建比 int8_t
更多的机器指令,因为它必须使用掩码和移位显式模拟溢出。
在我现在处理的代码中,某些大小是严格正数的。这些表示为size_t
。这看起来不错,因为这表明它们不可能是负面的。另一方面,不需要定义的模运算,所以只要有符号整数足够大(我们去像 200),无符号整数就会有错误的接口来进行我们想要的算术运算。
在代码中的某个点,存在从 0 到这个大小的循环。然后减去循环索引,取绝对值。
当我使用更现代的 GCC 7 编译它时,它无法解决 std::abs
的正确重载,因为显然 size_t - size_t
给出了模棱两可的值。我已将代码更改为在循环索引中使用 int
:
for (int t1 = 0; t1 < Lt; t1++)
for (int t2 = 0; t2 < Lt; t2++)
现在abs(t1 - t2)
工作正常。但是比较 t1 < Lt
会发出警告,因为它是有符号数和无符号数之间的比较。
什么是正确的方法?
-
对非负数使用无符号整数,然后在需要减法时使用
static_cast<int>()
。
循环索引使用有符号整数,容器大小使用无符号整数。然后在比较中使用static_cast<int>
。
在任何地方都使用有符号整数。当其他库返回无符号整数时,只需在此处使用static_cast<int>
即可满足警告。
【问题讨论】:
如果你想要一个有符号的值,使用ssize_t
(注意额外的s
)而不是int
。
This 可能很有趣。
@Martin Ueding 所有这些考虑都没有意义。只是忽略他们。如果某些实体不能否定,则使用适当的无符号类型。
@VladfromMoscow:是的。然后执行for (unsigned int i=0; i<size-1; i++)
之类的错误 - 如果大小为 0,则循环 UINT_MAX 次。加上无符号整数禁用某些优化。这是一个糟糕的建议。
@juanchopanza:我想过这个,但没有写。不涉及模算术,在 Carruths 演讲之后,我认为无符号整数用于模算术而不是非负值。
【参考方案1】:
“在一次谈话中,Carruth 展示了 uint8_t 作为 bzip2 中的 for 循环索引如何在 x86 上创建比 int8_t 更多的机器指令,因为它必须使用掩码和移位显式模拟溢出。”
好吧,如果您可以使用任一类型,则 for-range 必须限制为 [0, 127]
。只需使用int
作为索引类型,然后。它根据定义是基本数学运算的自然类型,通常很好地映射到 CPU 寄存器。
使用针对最小存储优化的类型不会生成最快的数学运算,不会。这并不奇怪。您无法根据此类有缺陷的设置得出关于签名与未签名的结论。
“size_t
- size_t
给出了模棱两可的值”
嗯,它没有,但它确实使用了模运算。 size_t(1)-size_t(2)==size_t(-1)
,但 size_t(-1)
是最大可能值。这直接来自模块化数学的定义:x-1 < x
,除非x-1
环绕,因为x==0
。 (或等价于x+1>x
,x+1==0
除外)
因此调用abs(size_t(x))
也毫无意义,因为每个size_t
值都是正数。将有符号整数与size_t
进行比较同样会带来意想不到的后果。明确的演员表是好的,因为它们使后果清楚。
但是没有通用的解决方案可以自动确定应该应用哪个演员表。如果可以发明机械规则,我们可以将该规则留给编译器。我们没有,因为我们做不到。作为程序员,您必须以数字方式考虑每种情况。
【讨论】:
以上是关于大小应该使用有符号整数还是无符号整数?的主要内容,如果未能解决你的问题,请参考以下文章