从 signed/unsigned char 到 unsigned/signed int 类型转换的 IA32 汇编代码

IA32 Assembly code for type casting from signed/unsigned char to unsigned/signed int

这是从程序员的角度看计算机系统(第 2 版)

在问题 3.4 中,要求学生确定从源数据类型转换为指针所指目标所需的汇编指令

本章后面给出的答案表明我们需要将源类型(左列)扩展到目标类型(中间列)。

没有太多信息可以从这个问题中得出规律。扩展的类型总是由源类型而不是目标类型确定的情况吗,如本例所示?

答案在你的书里

When performing a cast that involves both a size change and a change of “signedness” in C, the operation should change the size first. (Section 2.2.6).

-- CSAPP 2e, Practice Problem 3.4

所以这两个转换是这样的:

char --> int --> unsigned int
unsigned char --> unsigned int --> int

Is it always the case that the type of extension is determined by the source type and not the destination type, as in this example?

是的。

转换保留由原始位表示的(如果可能)。规则由此而来,所以这是关键概念这让你思考为什么其他一切都是这样。

  • 如果它是无符号的,您可以通过在左侧填充零来使其变宽。
    (零扩展:movzx,AT&T movzbl / movzwl / movl(x86-64 上的 dword 到 qword))
  • 如果它是有符号的,您可以通过填充符号位的副本来使其变宽。
    (2's complement sign extension1: movsx, 或 AT&T movsbl / movswl / movsb/wq)
  • 对于任何值,只需截断即可使其变窄
    (从内存中读取低字节,或者更窄的部分寄存器)

做任何其他事情都不会保值。例如对于 narrow unsigned -> wider signed,较宽的类型可以表示较窄类型的每个可能值。结果必须是非负的,所以符号位必须为零。 除了符号位,2 的补码和无符号二进制位值是相同的。 所以零扩展有效。例如192 作为 uint8_t192 作为 int 相同,但在 int.

左侧有更多零位

从 ISO C11 (n1570) 开始,转换规则非常合理并且与您的教科书显示的内容相匹配:

6.3 Conversions

6.3.1.3 Signed and unsigned integers

  1. When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
  2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)
  3. Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

对于第 2 点,请注意 "as if by repeatedly adding or subtracting one more than MAX_INT"(对于 2 的补码)等同于简单地将位模式重新解释为有符号。即 将 int 转换为无符号不需要 asm 操作,您只需 mov 甚至更好地读取该值已经存在的任何寄存器或内存。

有关此的更多详细信息:无符号类型不能表示负值,因此在这种情况下,负输入不能 "value preserving" 即使它们更窄。你可以把它看成逻辑上符号扩展为等宽有符号类型(可以表示值),然后转换为无符号(只是重新解释相同的位,即扩展转换后没有额外的asm指令)。


脚注 1:x86 使用 2 的补码,其他所有相关内容也是如此。 C++ 甚至正在考虑放弃使用 sign/magnitude 或 1 的补码整数的实现选项。


另请注意,教科书的答案使用了 movz/sbl 的不可编码形式。 它们仅适用于注册目的地。 movzbl %al, %ebx 可以,movzbl (%esi), %ebx 也可以,但是 movzbl %al, (%ebx) 不行

还相关: