我的C程序非常简单,可以在64位Linux中运行:
#include <stdio.h>
int main(void)
{
unsigned char a = 0xff;
unsigned short b = (a << 6) ;
return 0;
}
我很好奇(&&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&quot;&#6)的临时结果存储在哪里
0x0000000000400474 <+0>: push %rbp
0x0000000000400475 <+1>: mov %rsp,%rbp
0x0000000000400478 <+4>: movb $0xff,-0x3(%rbp)
0x000000000040047c <+8>: movzbl -0x3(%rbp),%eax
0x0000000000400480 <+12>: shl $0x6,%eax
0x0000000000400483 <+15>: mov %ax,-0x2(%rbp)
0x0000000000400487 <+19>: mov $0x0,%eax
0x000000000040048c <+24>: leaveq
0x000000000040048d <+25>: retq
从汇编代码中,我可以看到eax
用于存储班次操作的临时结果。
所以我想知道使用有符号/无符号32位整数来存储移位操作的临时结果是C标准吗?
我已检查n1570 specification的6.5.7
按位移位运算符,但找不到任何内容。
答案 0 :(得分:5)
标准没有指定那些低级详细信息,但是您错过了the draft C11 standrd部分6.5.7
段 2 中的一个重要部分(强调我的 >):
对每个操作数执行整数提升。[...]
部分6.3.1
算术操作数小节6.3.1.1
布尔,字符和整数段 2 其中说:
在int或unsigned int可以的任何地方都可以使用以下代码 使用:
并包含以下项目符号:
具有整数类型的对象或表达式(int或unsigned int除外) 其整数转换等级小于或等于int和的等级 unsigned int。
包括简短和 char 并接着说(强调我的):
如果int可以表示原始类型的所有值(由宽度限制,对于a bit-field),将值转换为int;否则,它将转换为无符号 INT。 这些称为整数促销。 58) 所有其他类型均未被 整数促销。
因此,a
和6
都必须提升为 int ,这将提供一个下限,可以使用哪个寄存器来存储中间结果。
注意,正如Keith在您的代码示例中指出的那样,因为赋值和移位没有可观察的行为,编译器可以自由地优化这些指令。
答案 1 :(得分:3)
C编译器可以将结果存储在任何喜欢的位置:您将a
指定为char
(通常为8位),将b
指定为short int
(通常为16位),它们都很容易适合32位%eax
。
如果将a
和b
定义为long long int(64位),您将注意到编译器必须使用64位寄存器(例如%rax
)。< / p>
答案 2 :(得分:3)
标准仅指定C程序的可观察行为。由于你的程序没有,编译器完全有权用nop
或几乎任何没有可观察到的副作用的东西替换它。
但是,通常小于int
的积分表达式的操作数被提升为int
或其他操作数类型,以较大者为准。
答案 3 :(得分:0)
实际上,对于移位操作,C标准要求将操作数符号扩展为int,进行计算并截断结果(如果需要)。如果它得到相同的结果,它只能省略它。
这就是为什么即使挑选编译器也不会发出警告,如果你移动一个超过7位的字符(尝试它 - 只有当你移动宽度大于允许时才会得到警告)。 / p>