在使用有符号整数和无符号整数表达式进行比较时,是否会导致错误?

8
通常我使用的对象会有(签名)int参数,例如int iSize,最终会存储某些东西的大小。同时,我通常会将它们初始化为-1,表示该对象(等等)尚未设置/未填写/尚未准备好使用。
当我执行像if(iSize>=someVector.size()){...}这样的操作时,经常会出现警告“在有符号和无符号整数之间比较”。
因此,名义上我不想使用unsigned int 。是否存在任何会导致错误或意外行为的情况?
如果没有:最好的处理方式是什么?如果我使用编译器标志-Wno-sign-compare,我可能会错过一个应该使用unsigned int(或类似的类型)的情况。所以我应该在与unsigned int进行比较时使用强制转换吗——例如if(iSize>=(int)someVector.size()){...}

1
当然,这可能会导致意外的(但已定义的)结果。 - WhozCraig
1个回答

5
是的,有一些非常微妙的问题。如果您感兴趣,可以查看Stephan T. Lavavej关于算术转换和Microsoft STL实现中由于有符号与无符号比较引起的错误的这个有趣的演示
总的来说,问题在于由于补码算术,一个非常小的负整数值具有与一个非常大的无符号整数值相同的位表示(例如-1 = 0xFFFF = 65535)。
在检查size()时,为什么不一开始就使用类型size_t来定义iSize?无符号值只是给您更大的表达能力,请使用它。
如果您不想将iSize声明为size_t,只需使用显式转换清楚地表明您知道此比较的性质即可。编译器试图通过这些警告帮助您,并且正如您正确写的那样,忽略它们可能会导致非常糟糕的后果。
因此,如果iSize有时为负数(并且应该评估为小于所有size()unsigned int值),请使用这个习惯用法:if ((iSize < 0) || ((unsigned)iSize < somevector.size())) ...

1
@AndyProwl,通常情况下都是适用的,因为你通常不想在普通情况下使用“unsigned”。根据平台(或为了确保以后的可移植性),您可能需要编写并使用“checked_cast”,它将验证您要转换的值是否在范围内。 - James Kanze
1
@zhermes:你“绝对”确定将size()的结果(可能是unsigned long)强制转换为signed int不会导致负数吗?你有多么确定?是-1%确定吗? :) - rici
2
@AndyProwl:这会让您重新评估“使用强制转换:if(iSize >= (int)someVector.size())”的智慧吗?鉴于您的假设“如果iSize有时为负数(并且应将其评估为小于所有大小的无符号整数值)”,我会说给出正确的表达式是if (iSize < 0 || (unsigned)iSize < somevector.size()),这是一个明确陈述规定目标。我坚持认为,用“unset”指示符重载iSize是次优的。 - rici
@JamesKanze:我希望看到一些努力来标准化C语言的方言,使代码能够指定数字类型并满足特定要求(例如允许代码指定大端、小端或“任何方便的方式”,区分基数和代数环等),目标是几乎所有定义良好的代码在当前方言和新方言下都可以编译,并且大多数在当前方言下编译但在新方言下无法编译的代码可以轻松地适应于两种方言的相同编译。 - supercat
@JamesKanze: 我真的看不出来,在没有指定类型的情况下,int 怎么可能扩展到超过 32 位,除非有一种指定为模数为 2 ^ 32 的整数代数环的行为的类型 [与 int 的大小无关],并且为代数环、基数和“遗留无符号整数”分别设置类型,可以让我们根据适用于这些类型的规则定义代数环和基数的规则,而不是基于编译器似乎正在做什么组合临时规则(我认为这就是最初有符号/无符号规则的产生方式)。 - supercat
显示剩余18条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接