5

我所说的“直觉”是指给定的

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

表达式(a < b)应计算为 1。

Stackoverflow 上有很多问题已经在询问为什么在这种或那种特殊情况下 C 编译器会抱怨有符号/无符号比较。答案归结为整数转换规则等。然而,在比较有符号整数和无符号整数时,为什么编译器必须如此愚蠢,似乎没有任何理由。使用上面的声明,为什么表达式喜欢

(a < b)

不会自动替换为

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

如果没有单一的机器指令可以正确执行?

现在,有一些关于“如果你必须混合有符号和无符号整数,你的程序有问题”的问题的评论。我不会买这个,因为 libc 本身就不可能生活在一个只有签名或无符号的世界中(例如sprintf(),函数系列的示例返回int为写入的字节数,send()返回ssize_t等等)。

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

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

4

4 回答 4

6

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

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

对于 ASCII 空间和许多其他字符,您建议的替换将变为 true。

顺便说一句,我相信这个问题部分是重复的:

如果我们添加安全的有符号/无符号比较 C/C++,它会破坏语言或现有代码吗?

于 2013-01-23T17:33:53.863 回答
1

在这种情况下,我确信它会再次退回到 C(和 C++),而不是让您为不需要的功能付费。如果默认行为令人满意,您只需编写显而易见的代码。如果这还不足以满足您的需求,那么您自己编写两部分表达式,然后再付出额外的代价。如果编译器总是按照您的建议执行,您最终可能会付出代码性能损失,即使程序中使用的实际值范围永远不会导致任何问题。

然后,一些编译器会为您提供方便/不正确的警告,让您知道您已进入比较不同符号值的区域。

于 2013-01-23T17:44:11.617 回答
1

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

对于有符号和无符号类型之间比较的非常特殊的情况,可能会出现您建议的异常。代价将是处理表达式操作数的规则中的不规则性和复杂的实现 - 签名

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

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

于 2013-01-23T18:04:09.847 回答
0

如果我没记错的话,这只是一个警告,因此可以忽略。

问题是整数变体的范围。

虽然有符号整数可以保存从 -2147483648 到 2147483648(+- 一或二)的值,但无符号整数的范围可以从 0 到 4294967296。

这意味着,如果将有符号整数与无符号整数进行比较,可能会导致完全错误的结果,因为在内部,符号由整数的 MSB 表示。

一个例子:

您有数字 -1 和数字 3,000,000,000。哪个更大?显然,您可能会说第二个......但对于计算机,-1 实际上更大,因为“无符号”(正确评估大的需要),-1 表示为最大数。(4294967296)。

相反,如果两者都被视为有符号,则大数将是一些相当大的负数,因为它超出了有符号整数的范围。

这就是编译器输出此警告的原因。虽然实际的错误情况相当罕见,但它仍然可能发生。这正是编译器警告你的……当比较两个不同符号的整数时,可能会发生一些意想不到的事情。

于 2013-01-23T17:32:08.347 回答