【问题标题】:How memory store signed and unsigned char内存如何存储有符号和无符号字符
【发布时间】:2020-08-09 00:35:05
【问题描述】:

刚开始学C,有点迷茫。

我有一些问题:

  1. 如果我有以下代码:
signed  char x = 56;
// ‭In the RAM, I will see 00111000‬ yes/no?

signed char z = -56;
// In the RAM, I will see 11001000 yes/no?

unsigned char y = 200;
// ‭In the RAM, I will see 11001000‬ yes/no?
  1. 我有以下代码:
if (z<0){
   printf("0 is bigger then z ");
}

编译后,汇编指令如何知道z是-56而不是200?(有符号和无符号的特殊ASM指令?)。

正如我在问题 1 中提到的,z 和 y 的值是 11001000,并且没有任何指示知道它是有符号还是无符号。

抱歉,如果我没有找到正确的方式来问我的问题,希望你能理解我 谢谢

【问题讨论】:

  • (1) 是,(2) 编译器知道变量的类型,并会生成汇编语言来处理它们。
  • 我建议您选择最能回答您的问题的已发布答案,如果您觉得满意,请“接受”(点击接受检查)。

标签: c assembly char unsigned signed


【解决方案1】:

计算机不知道也不关心这些事情。无符号和有符号只与程序员有关。值 0xFF 同时可以是 -1、255 或地址或地址的一部分。浮点数的一部分等等。电脑不在乎。程序员如何通过程序传达他们对位的解释。了解加法和减法也不关心有符号与无符号,因为它是相同的逻辑,但其他指令,如结果大于输入的乘法或结果小于输入的除法,则有无符号和有符号版本的指令或您的处理器可能只有一个,您必须合成另一个(或没有,您必须合成两者)。

int fun0 ( void )
{
    return(5);
}
unsigned int fun1 ( void )
{
    return(5);
}
00000000 <fun0>:
   0:   e3a00005    mov r0, #5
   4:   e12fff1e    bx  lr

00000008 <fun1>:
   8:   e3a00005    mov r0, #5
   c:   e12fff1e    bx  lr

没有特殊的位或命名法……位就是位。

编译器由用户驱动的高级语言中的有符号与无符号指示驱动指令和数据值导致alu通过单个标志或组合输出指示大于、小于和等于的标志,然后是条件可以根据标志采取分支。通常但并非总是,如果 z = 0 则编译器会说,然后跳过某事。

【讨论】:

    【解决方案2】:

    有符号和无符号的特殊 ASM 指令吗?

    是的,硬件一般都有机器码指令(或指令序列),可以

    • 符号将字节扩展为字大小
    • 零将字节扩展到字大小
    • 比较各种关系的符号数量, >=
    • 比较各种关系的无符号量, >=

    汇编指令如何知道 z 是 -56 而不是 200?

    在高级语言中,我们将类型与变量相关联。从那时起,编译器就知道解释使用该变量的代码的默认方式。 (我们可以在使用变量时使用强制转换来覆盖或更改默认解释。)

    在机器代码中,只有字节,无论是在内存中还是在 CPU 寄存器中。因此,重要的不是它是如何存储的(对于有符号还是无符号),而是使用什么指令来访问存储。每次访问变量时,编译器都会使用正确的机器代码指令集。

    虽然我们在内存中存储了很多东西,但处理器没有变量声明的概念。处理器只能看到机器代码指令,并通过被告知要执行的指令来解释所有数据类型。

    作为汇编程序员,您的工作是在每次使用相同的变量时应用正确的指令(此处为有符号与无符号)。使用一个字节作为有符号变量,后来又作为无符号变量,这是一个在汇编语言中很容易做到的逻辑错误。

    如果您使用错误的大小来访问变量,一些汇编程序会有所帮助,但如果您使用正确的大小但签名不正确,我知道没有任何帮助。

    【讨论】:

      【解决方案3】:

      编译器将为有符号和无符号情况生成适当的指令。我认为最好看一个例子。以下代码

      void foobar();
      
      void foo(unsigned char a)
      {
          if (a < 10)
              foobar();
      }
      
      void bar(char a)
      {
          if (a < 10)
              foobar();
      }
      

      将使用 -O3 标志转换为带有 GCC 5.4 的 MIPS 代码。

      foo:
              andi    $4,$4,0x00ff
              sltu    $4,$4,10
              bne     $4,$0,$L4
              nop
      
              j       $31
              nop
      
      $L4:
              j       foobar
              nop
      
      bar:
              sll     $4,$4,24
              sra     $4,$4,24
              slt     $4,$4,10
              bne     $4,$0,$L7
              nop
      
              j       $31
              nop
      
      $L7:
              j       foobar
              nop
      

      这是foo 函数(使用unsigned char 类型)的有趣部分

      foo:
              andi    $4,$4,0x00ff
              sltu    $4,$4,10
      

      如您所见,使用的sltu 命令是slt 的未签名版本。 (您实际上不必知道它的作用)

      如果我们查看函数 bar 相关部分

      bar:
              sll     $4,$4,24
              sra     $4,$4,24
              slt     $4,$4,10
      

      您可以看到slt 使用了它将其寄存器操作数视为有符号。 sllsra 对进行符号扩展,因为这里操作数 a 已签名,因此需要它,而在未签名的情况下则不需要。

      因此您可以看到针对操作数的符号生成了不同的指令。

      【讨论】:

      • 请注意,某些 ABI 具有 char = unsigned char,因此最好将您的签名示例写为 signed char。显然,对于您选择的 ABI (MIPS),char = signed char 当然,但如果有人想在 ARM 上尝试相同的东西,他们会为这两个函数获得相同的代码。
      • 在未签名的情况下说不需要扩展有点误导。 sign 扩展不是必需的,但andi $4,$4,0x00ff 正在执行从unsigned charintzero-扩展。 (或者更具体地说,slt 的寄存器宽度/操作数大小,因为 MIPS 没有只查看 reg 的低 8 位的指令。)我认为 MIPS 调用约定通常传递/返回窄参数符号-扩展到 32 位,但显然 GCC 并没有假设。
      【解决方案4】:

      数字以二进制形式存储。负数通常以二进制补码形式存储,但 C 语言允许不同的表示形式。所以这个:

      signed char z = -56;
      // In the RAM, I will see 11001000 yes/no?
      

      通常是的,但可能不在某些特殊平台上。

      第二个问题过于具体。例如,在 x86 上与零的比较可以作为自比较执行,并且flags 寄存器会受到影响,因为无符号比较符号标志 (SF) 会被忽略。

      【讨论】:

        【解决方案5】:

        编译器会根据它是无符号类型还是有符号类型生成不同的指令。这就是告诉处理器以哪种方式处理它的原因。所以是的,有签名和未签名的单独说明。对于 Intel 处理器,根据宽度(char、short、int)也有单独的指令

        【讨论】:

        • @ALevy:请注意,大多数指令对于有符号 2 的补码或普通二进制无符号是相同的。就像add eax, 1 一样,两者都是一样的。只有比较、右移和除法不同。例如有符号比较看 OF != SF 的小于与大于。 teaching.idallen.com/dat2343/10f/notes/040_overflow.txt 描述了 ALU 如何计算相同加法的无符号进位和有符号溢出结果。这取决于它所查看的标志的软件。
        猜你喜欢
        • 1970-01-01
        • 2013-12-29
        • 1970-01-01
        • 1970-01-01
        • 2016-08-27
        • 1970-01-01
        • 1970-01-01
        • 2019-01-15
        • 1970-01-01
        相关资源
        最近更新 更多