【问题标题】:How does in assembly does assigning negative number to an unsigned int work?在汇编中如何将负数分配给 unsigned int 工作?
【发布时间】:2017-05-14 22:09:37
【问题描述】:

我了解了 2's Complement 以及 unsigned 和 signed 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 在 unsigned 和 signed 中都被移到了寄存器中。我想知道如果重新解释只是重要的,那么为什么我们有两种类型 unsignedsigned ,它是 printf 格式字符串 %d%u 这很重要?

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

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

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

【问题讨论】:

  • “Machine 如何知道何时必须进行 2 的补码,何时不需要” - 我不明白 - 一切都归结为编程中的二进制。机器指令不关心 signedunsigned 这些是更高级别的构造。
  • @UnholySheep 我的意思是当我写 signed int a = -1 时,它被存储为 2 的补码,所以每个负数无论是否以这种方式存储在哪里?
  • 对于C语言,负数可以是三种表示之一,所以不一定是2的补码。 printf 格式也必须与输入匹配,否则您的程序格式不正确。当前的 C 标准支持 2 的补码,即在有符号类型与无符号类型之间的转换不会改变本机 2 的补码计算机上的位表示。当变量在寄存器中时,机器真的不知道变量的符号性。编译器知道,程序也知道,这就够了。
  • @EOF 我很好地提出了这个问题,但人们仍然来投反对票,甚至没有告诉 OP 一旦他们的问题出了什么问题,

标签: c assembly unsigned unsigned-integer conceptual


【解决方案1】:

当您尝试将负值分配给更大的无符号整数时,您会更好地理解它。编译器在将小尺寸负值转换为大尺寸无符号整数时生成汇编代码以进行符号扩展。

see this blog post for assembly level explanation.

【讨论】:

    【解决方案2】:

    您似乎忽略了以下事实:首先,有符号和无符号整数值中的 0101 = 5,其次,您为无符号整数分配了一个负数 - 您的编译器可能足够聪明地意识到这一点,并且,因此,更正为带符号的 int。

    从技术上讲,将 unsigned int 设置为 -5 会导致错误,因为 unsigned int 不能存储小于 0 的值。

    【讨论】:

      【解决方案3】:

      有符号整数表示的选择由平台决定。该表示适用于负值和非负值 - 例如,如果 1101<sub>2</sub> (-5) 是 0101<sub>2</sub> (5) 的二进制补码,则 0101<sub>2</sub> (5) 也是 1101<sub>2</sub> (-5) 的二进制补码。

      平台可能会或可能不会为有符号和无符号整数的操作提供单独的说明。例如,x86 为有符号(idivimul)和无符号(divmul)整数提供不同的乘法和除法指令,但使用相同的加法(add)和减法(sub) ) 两者的说明。

      同样,x86 为有符号和无符号整数提供了一个比较 (cmp) 指令。

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

      printf而言,转换说明符确定位模式0xFFFF显示-1还是4294967295是绝对正确的,尽管请记住如果参数的类型与转换说明符所期望的不匹配,则行为未定义。使用 %u 显示负数 signed int 可能会也可能不会给您预期的等效无符号值。

      【讨论】:

      • initializer 将 int 类型更改为无符号类型是什么意思,什么时候发生?
      • 这个问题是否足以产生 -2 ,并进一步阻止我在接下来的 6 个月内发布任何问题。
      【解决方案4】:

      有符号和无符号类型有一些区别:

      1. 运算符&lt;&lt;=&gt;&gt;=/%&gt;&gt;在处理有符号数和无符号数时的行为都是不同的。

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

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

      unsigned mul(unsigned short x, unsigned short y) { return x*y; }
      

      可能假设不会出现乘积超过 2147483647 的情况。例如,如果它看到调用的函数为 unsigned x = mul(y,65535);yunsigned short,它可能会省略其他地方的代码,这些代码只会是如果y 大于 37268,则相关。

      【讨论】:

      • 我们可以聊聊先生吗?
      • @SurajJain:当然。告诉我在哪里。
      【解决方案5】:

      有符号和无符号都是内存片段,根据操作,它们的行为方式很重要。

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

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

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

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

      另外,请注意,C 对带符号数的存储方式是宽松的,它们不必是 2-补码。但是现在,所有常见的架构都是这样存储签名的。

      【讨论】:

      • 当我将 -1 分配给 unsigned int 时,会发生什么,在汇编中它显示为 signed 和 unsigned int 相同。
      • @SurajJain 这是因为在 2 的补码表示中,值 相同的。它们是相同的二进制位模式。正是它们的使用方式使它们与众不同。
      • @WeatherVane 我要问的是每个负数,都表示为 2 的补码,如果我将负数存储在 unsigned int stackoverflow.com/a/7152835/5473170 中,它会写入初始化程序将此值从 int 转换为 unsigned int .它发生在哪里?
      • @WeatherVane 更准确地说,当前的 C 标准要求将负数转换为无符号数以遵循“2 的补码规则”,这意味着如果计算机本身使用“2 的补码”,则无需执行任何操作.
      • 位模式已写入。然后将其解释为不同的类型。但它是相同的位模式。打印为int,即-1。打印为unsigned,即4294967295
      猜你喜欢
      • 2019-10-07
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      • 2017-07-30
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      相关资源
      最近更新 更多