“直觉”我指的是
int a = -1;
unsigned int b = 3;
表达式 (a < b)
应该评估为1 .
Stackoverflow上有很多问题已经问过为什么在这个或那个特殊情况下C编译器会抱怨签名/无符号比较 . 答案归结为整数转换规则等 . 然而,在比较有符号整数和无符号整数时,为什么编译器必须如此格外愚蠢似乎并不存在 rationale . 使用上面的声明,为什么表达式像
(a < b)
不会自动替换
(a < 0 || (unsigned int)a < b)
如果没有单一机器指令正确执行?
现在,已经对"if you have to mix signed and unsigned integers, there is something wrong with your program"中的先前问题进行了一些评论 . 我不会买,因为libc本身使得无法生活在仅有符号或无符号的世界中(例如, sprintf()
函数系列函数返回 int
作为写入的字节数, send()
返回 ssize_t
等等) .
我也不认为我可以购买下面评论中表达的想法 implicit 将有符号整数转换为无符号(#889554_ "idiom"),与 explicit cast( ((unsigned int)(d - '0') < 10U)
)相比,在C程序员身上赋予了一些额外的权力 . 但是肯定会有很多机会搞砸了 .
是的,我很高兴编译器警告我它不能这样做(不幸的是,只有我明确地问它) . 问题是 - 为什么不能呢?通常标准规则背后有充分的理由,所以我想知道这里有没有?
4 回答
无法进行自动替换,因为它与C语义不同,并且会严重破坏正确使用转换的程序 . 例如:
对于ASCII空间和许多其他字符,您的建议替换将成为现实 .
顺便说一句,我认为这个问题部分与以下问题重复:
Would it break the language or existing code if we'd add safe signed/unsigned compares to C/C++?
在这种情况下,我确信它会再次回到C(和C)而不是让你为不需要的功能付费 . 如果默认行为令人满意,您只需编写明显的代码即可 . 如果它不足以满足您的需求,那么您自己编写两部分表达式,然后再支付额外费用 . 如果编译器始终按照您的建议执行操作,那么即使程序中使用的实际值范围永远不会导致任何问题,也可能最终会支付代码性能损失 .
然后,一些编译器会为您提供方便/无正确的警告,以告知您已进入比较不同签名值的区域 .
通常的算术转换规则适用于几乎所有二元运算符的操作数 . 它们是一个统一的框架,用于处理不同大小的整体类型和操作中的签名(至少在机器级别)需要相同的类型 . 这些规则旨在使通用计算机体系结构上的实现尽可能简单和高效 . 特别是有符号和无符号int之间的转换通常是两个补码架构上的无操作,并且比较仍然是单个指令 - 有符号或无符号 .
对于有符号和无符号类型之间比较的特殊情况,可能会出现类似于您建议的异常 . 处理表达式操作数的规则和复杂的实现(签名)将导致成本不规范
C的设计者选择不这样做 . 改变这个决定会破坏许多现有代码以获得有限的好处 - 您仍然会遇到与其他运算符的常见算术转换,因此您必须了解它们 .
编译器警告(或可以警告)可能产生令人惊讶的结果的转换,这样您就不会对意外混合的不同符号或大小的整数感到惊讶 . 使用强制转换来准确表达您希望如何评估它 - 消除警告并帮助代码的下一位读者 .
如果我没有弄错的话,这只是一个警告,因此可以被忽视 .
问题是整数变体的范围 .
有符号整数可以保存从-2147483648到2147483648( - 一个或两个)的值,无符号整数的范围可以是0到4294967296 .
这意味着,如果将有符号整数与无符号整数进行比较,则可能会导致完全错误的结果,因为内部符号由整数的MSB表示 .
一个例子:
您的数字为-1,数字为3,000,000,000 . 哪一个更大?很明显,你可能会说第二个......但对于计算机来说,-1实际上更大,因为'为无符号'(正确评估大的那个),-1表示为最大数 . (4294967296) .
相反,如果两者都被视为有符号,则大数将是一些相当高的负数,因为它超出了有符号整数的范围 .
这就是编译器输出此警告的原因 . 虽然实际的错误情况相当罕见,它仍然可能发生 . 而这正是编译器警告你的......当比较两个不同的有符号整数时,可能会发生意外情况 .