转换有符号整数与短整数时符号扩展不一致
int main(){
signed int a = 0b00000000001111111111111111111111;
signed int b = (a << 10) >> 10;
// b is: 0b11111111111111111111111111111111
signed short c = 0b0000000000111111;
signed short d = (c << 10) >> 10;
// d is: 0b111111
return 0;
}
假设int是 32 位和short16 位,
为什么会b得到符号扩展但d没有得到符号扩展?我已经在 x64 上用 gdb 测试过这个,用 gcc 编译。
为了获得short符号扩展,我不得不使用两个单独的变量,如下所示:
signed short f = c << 10;
signed short g = f >> 10;
// g is: 0b1111111111111111
回答
在 的情况下signed short,当int在表达式中使用小于 的整数类型时,它(在大多数情况下)被提升为type int。这在C 标准的第 6.3.1.1p2 节中有详细说明:
以下可用于表达式中的任何地方
int或
unsigned int可使用的地方
- 具有整数类型(除了
intorunsigned int)的对象或表达式,其整数转换等级小于或等于intand的等级unsigned int。_Bool,int,signed int, or类型的位域unsigned int。如果 an
int可以表示原始类型的所有值(受宽度限制,对于位域),则将该值转换为int; 否则,它被转换为
unsigned int。这些被称为整数提升所有其他类型都不会被整数提升
这种提升特别发生在第 6.5.7p3 节中指定的按位移位运算符的情况下:
对每个操作数执行整数提升。 结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。
因此short值 0x003f 被提升为int值 0x0000003f 并应用左移。结果是 0x0000fc00,右移的结果是 0x0000003f。
这个signed int案子有点意思。在这种情况下,您将值为 1 的位左移到符号位。根据 6.5.7p4,这会触发未定义的行为:
的结果
E1 << E2是E1左移位E2位置;空出的位用零填充。如果E1具有无符号类型,则结果的值为E1×2 E2,比结果类型中可表示的最大值减少模一。 如果E1有符号类型和非负值,并且E1×2 E2在结果类型中是可表示的,那么就是结果值;否则,行为未定义。
因此,虽然您为signed int案例获得的输出与您所期望的一样,但它实际上是未定义的行为,因此您不能依赖该结果。