汇编中如何为无符号整数分配负数?

时间:2016-12-30 16:00:53

标签: c assembly unsigned unsigned-integer conceptual

我了解了2's Complement和未签名并签名的int。所以我决定测试我的知识,据我所知,负数以2's complement的方式存储,这样加法和减法就不会有不同的算法和电路会很简单。

现在如果我写

int main()
{
  int a = -1 ;
  unsigned int b = - 1 ;

  printf("%d %u \n %d %u" , a ,a , b, b);
}

输出结果为-1 4294967295 -1 4294967295。现在,我查看了比特模式和各种事情,然后我意识到2的补码中的-111111111 11111111 11111111 11111111,所以当我用%d解释它时,它给出-1,但是当我使用%u进行解释,将其视为正数,因此它会给出4294967295。我检查了程序集的代码是

.LC0:
    .string "%d %u \n %d %u"
main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    mov     DWORD PTR [rbp-4], -1
    mov     DWORD PTR [rbp-8], -1
    mov     esi, DWORD PTR [rbp-8]
    mov     ecx, DWORD PTR [rbp-8]
    mov     edx, DWORD PTR [rbp-4]
    mov     eax, DWORD PTR [rbp-4]
    mov     r8d, esi
    mov     esi, eax
    mov     edi, OFFSET FLAT:.LC0
    mov     eax, 0
    call    printf
    mov     eax, 0
    leave
    ret

现在,-1在无符号和签名时都被移动到寄存器中。我想知道重新解释是否重要,那么为什么我们有两种类型unsignedsigned,它是printf格式字符串%d和{{1}那重要吗?

当我将负数分配给无符号整数时,真正发生了什么(我了解到初始值设定项将此值从%u转换为int。)但是在汇编代码中我没有看到这样的事情。那真的发生了什么?

机器如何知道何时必须unsigned int,何时不知道,它是否看到负号并执行2's complement

我已经阅读了几乎所有的问题和答案,你可以认为这个问题是重复的,但我找不到一个令人满意的解决方案。

5 个答案:

答案 0 :(得分:2)

签名和未签名都是内存片段,根据操作,它们的行为方式很重要。

添加或减去时没有任何区别,因为由于2-complement,操作完全相同。

当我们比较两个数字时很重要:-1低于0而4294967295显然不是。

关于转换 - 对于相同的大小,它只需要变量内容并将其移动到另一个 - 所以4294967295变为-1。对于更大的尺寸,它首先签署扩展,然后内容移动。

现在机器如何 - 根据我们使用的说明。机器具有用于比较有符号和无符号的不同指令,或者它们为它提供不同的标志(x86具有无符号溢出的进位和有符号溢出的溢出)。

此外,请注意C放松了签名号码的存储方式,它们不一定是2补码。但是现在,所有常见的架构都存储了这样的签名。

答案 1 :(得分:0)

有符号和无符号类型之间存在一些差异:

  1. 运营商<<=>>=/%和{{}的行为处理有符号和无符号数时,1}}都不同。

  2. 如果对有符号值的任何计算超出其类型范围,则编译器不需要以可预测的方式运行。即使在所有定义的情况下使用与有符号和无符号值相同的运算符,一些编译器也会以“有趣”的方式运行。例如,如果>>已签名,则给定x+1 > y的编译器可以用x>=y替换它,但如果x未签名则不能替换它。

  3. 作为一个更有趣的例子,在“short”为16位且“int”为32位的系统上,编译器给出了函数:

    x

    可能会假设产品不会超过2147483647的情况。例如,如果它看到调用的函数为unsigned mul(unsigned short x, unsigned short y) { return x*y; } unsigned x = mul(y,65535);y,则可能会忽略其他地方的代码只有在unsigned short大于37268时才有意义。

答案 2 :(得分:0)

似乎你似乎已经错过了第一个事实,即有符号和无符号整数值中的0101 = 5,其次,你为无符号整数赋予了一个负数 - 你的编译器可能足够聪明,并且,因此,对签名的int更正。

将unsigned int设置为-5在技术上会导致错误,因为无符号整数不能将值存储在0以下。

答案 3 :(得分:0)

当您尝试将负值分配给较大的无符号整数时,您可以更好地理解它。当将小尺寸负值传送到较大尺寸的无符号整数时,编译器会生成汇编代码以执行符号扩展。

see this blog post for assembly level explanation.

答案 4 :(得分:-1)

签名整数表示的选择留给平台。该表示适用于负值和非负值 - 例如,如果calc(100% - 64px);( - 5)是11012(5)的两个补码,那么01012(5)是< em>也 01012( - 5)的两个补码。

平台可能会也可能不会对有符号和无符号整数的操作提供单独的指令。例如,x86为带符号(11012idiv)和无符号(imuldiv)整数提供不同的乘法和除法指令,但使用相同的加法({{ 1}})和减法(mul)指令。

类似地,x86为有符号和无符号整数提供单个比较(add)指令。

算术和比较操作将设置一个或多个状态寄存器标志(进位,溢出,零等)。在处理应该表示有符号值和无符号值的单词时,可以使用不同的方法。

sub而言,转换说明符确定位模式cmp是否显示printf或{ {1}},但请记住,如果参数的类型与转换说明符所期望的不匹配,则行为未定义。使用0xFFFF显示否定-1可能会或可能不会为您提供预期的等效无符号值。