首页 文章

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

提问于
浏览
5

“直觉”我指的是

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 回答

  • 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++?

  • 0

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

    然后,一些编译器会为您提供方便/无正确的警告,以告知您已进入比较不同签名值的区域 .

  • 1

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

    对于有符号和无符号类型之间比较的特殊情况,可能会出现类似于您建议的异常 . 处理表达式操作数的规则和复杂的实现(签名)将导致成本不规范

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

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

  • 1

    如果我没有弄错的话,这只是一个警告,因此可以被忽视 .

    问题是整数变体的范围 .

    有符号整数可以保存从-2147483648到2147483648( - 一个或两个)的值,无符号整数的范围可以是0到4294967296 .

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

    一个例子:

    您的数字为-1,数字为3,000,000,000 . 哪一个更大?很明显,你可能会说第二个......但对于计算机来说,-1实际上更大,因为'为无符号'(正确评估大的那个),-1表示为最大数 . (4294967296) .

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

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

相关问题