为什么C编译器不能以直观的方式进行签名/无符号比较

时间:2013-01-23 17:25:25

标签: c++ c comparison unsigned signed

“直觉”我指的是

int a = -1;
unsigned int b = 3;

表达式(a < b)应评估为1。

Stackoverflow上有很多问题已经问过为什么在这个或那个特殊情况下C编译器会抱怨签名/无符号比较。答案归结为整数转换规则等。然而,在比较有边和无符号整数时,为什么编译器必须如此异常哑,似乎没有基本原理。使用上面的声明,为什么表达式像

(a < b)

不会自动替换为

(a < 0 || (unsigned int)a < b)

如果没有单一机器指令正确执行它?

现在,对于之前的问题有一些评论,“如果你必须混合有符号和无符号整数,你的程序就有问题”。我不会买,因为libc本身使得无法生活在仅有符号或无符号的世界中(例如,sprintf()函数系列函数返回int作为写入的字节数{{1 }}返回send()等等。

我也不认为我可以购买下面评论中表达的想法隐式将有符号整数转换为无符号(ssize_t“成语”)赋予一些额外的权力在C程序员上与显式强制转换((d - '0' < 10U))相比较。但可以肯定的是,它打开了很多机会搞砸了。

是的,我很高兴编译器警告我它不能这样做(不幸的是,只有我明确地询问它)。问题是 - 为什么不能呢?通常标准规则背后有很好的理由,所以我想知道这里有没有?

2 个答案:

答案 0 :(得分:6)

无法进行自动替换,因为它与C语义不同,并且会严重破坏正确使用转换的程序。例如:

if (d-'0'<10U)  // false if d is not a digit

对于ASCII空间和许多其他符合您建议替换的字符都将成为现实。

顺便说一下,我相信这个问题部分与以下内容重复:

Would it break the language or existing code if we'd add safe signed/unsigned compares to C/C++?

答案 1 :(得分:1)

通常的算术转换规则适用于几乎所有二元运算符的操作数。它们是一个统一的框架,用于处理不同大小的整体类型和操作中的签名(至少在机器级别)需要相同类型。这些规则旨在使通用计算机体系结构上的实现尽可能简单和高效。特别是有符号和无符号int之间的转换通常是两个补码架构上的无操作,并且比较仍然是单个指令 - 无论是有符号还是无符号。

像你建议的那样的异常可能是签名和无符号类型之间比较的特殊情况。处理表达式操作数的规则和复杂的实现(签名)将导致成本不规范

C的设计师选择不这样做。改变这个决定会破坏大量现有代码以获得有限的好处 - 您仍然会遇到与其他运算符的常见算术转换,因此您必须了解它们。

编译器警告(或可以警告)可能产生令人惊讶的结果的转换,这样您就不会对意外混合的不同符号或大小的整数感到惊讶。使用强制转换来准确表达您希望如何评估它 - 消除警告并帮助代码的下一位读者。