【问题标题】:Compare unsigned with literal比较无符号和文字
【发布时间】:2020-09-12 23:40:28
【问题描述】:

我读到here,有符号运算有不同的算术汇编指令。但是考虑代码:

unsigned char a = 0xff;
if(a == 0xff){
    // do something
}

我记得在拼贴中我编写了这样的代码,if 条件从来都不是真的,但它是在 AVR 上的。我在我的工作中看到过类似的东西,所以我把它输入到了godbolt,它显示了x86:

mov     BYTE PTR [rbp-1], -1
cmp     BYTE PTR [rbp-1], -1
jne     .L2

这是令人不安的,因为它表明,无符号修饰符被忽略。它是否受标准和 gcc 监管,只是为了优化目的而简化,实际上将 0xff 转换为无符号?

编辑:我最初的问题有些混乱(我没有提到我的示例是针对 8 位处理器的)所以这里有一个替代方案:

int main(){
    unsigned int a = 0xffffffff;
    if(a == -1)
        return 0;
    return 1;
}

翻译为:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], -1
        cmp     DWORD PTR [rbp-4], -1
        jne     .L2
        mov     eax, 0
        jmp     .L3
.L2:
        mov     eax, 1
.L3:
        pop     rbp
        ret

这(如果我理解正确的话)意味着 2^32-1 实际上等于 -1。

【问题讨论】:

  • 不忽略无符号修饰符。您希望得到哪些指令而不是这些指令?
  • unsigned char a = 0xff;之后a的值是255。0xff的值也是255。即使有转换,当255转换为unsigned char或到intunsigned int,值仍然是255。所以编译器会将255 与255 进行比较。你认为应该发生什么不同?
  • 你在 college 所做的可能是使用了 char,这是一个签名类型。
  • 我希望看到:movzx eax, BYTE PTR [rbp-1] cmp eax, -1
  • 但是0xff 不是(int)-1。它是一个小的正整数。所以cmp eax, -1 将实现 C 逻辑;这是错误的常数。但是,是的,movzx 加载将显式实现 C 整数提升规则,即将该操作数的值保持扩大到 ==int,匹配 0xff 的类型(作为数字文字默认为 int已经,不需要促销)。请注意,即使在 8 位处理器上,int 也由 ISO C 保证至少为 16 位宽,并且0xff 舒适地低于 INT_MAX。在优化扩展后,我们只能与-1 进行字节比较。

标签: c gcc x86


【解决方案1】:

对于 x86 中的有符号和无符号整数的大多数操作,有不同的指令。 -1 在 8 位中的表示是 0xff。所以显示的指令完全与常量写成 0xff 一样。

【讨论】:

  • P.S.最好不要查看未优化的编译器输出。这几乎是垃圾代码。
  • 好的,char 就是一个例子,因为在拼贴画时我正在使用 8 位处理器,但是如果有 -1(写入 0xffff..)占用整个内存单元怎么办?我在问这样的文字是否会被解释为 -1 或 2^n-1?所以,即如果我的条件永远是真的。
【解决方案2】:

我希望看到:movzx eax, BYTE PTR [rbp-1] / cmp eax, -1

但是0xff 不是(int)-1。它是一个小的正整数。所以cmp eax, -1 不会实现 C 逻辑;这是错误的常数。

是的,movzx 加载将显式实现 C 整数提升规则,即将该操作数的值保持扩大到 ==int(“通常的算术转换”)。这为您提供了与0xff 类型匹配的int(作为数字文字已经默认为int,在C 抽象机器中不需要提升)。所以movzx 部分是天真地实现 C 抽象机规则的有效部分。

Implicit type promotion ruleshttps://en.cppreference.com/w/c/language/conversion。请注意,在 C 抽象机中,基本上您可以对窄类型执行的所有操作都会在操作之前将其提升为 int,甚至像一元 - 否定。但是编译器通常可以优化回原始变量的实际操作数大小,以进行最终具有相同结果的操作。

请注意,即使在 8 位处理器上,int 也由 ISO C 保证至少为 16 位宽,而0xff 则轻松低于 INT_MAX。在优化扩展后,我们只能与-1 进行字节比较。


这(如果我理解正确的话)意味着 2^32-1 实际上等于 -1。

是的,x86 与所有现代 ISA 一样使用 2 的补码整数。 ISO C 允许 2 的补码、1 的补码或符号/大小。

(有趣的事实:C++20 最终放弃了其他的,只指定了 2 的补码。有符号溢出仍然是未定义的行为,但这是一个单独的问题......现代优化 C 和 C++ 实现与可移植汇编语言相去甚远。 )

【讨论】:

    猜你喜欢
    • 2014-01-28
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    • 2011-07-21
    • 2015-09-08
    • 2016-08-07
    • 1970-01-01
    相关资源
    最近更新 更多