无符号字符左移16位是什么意思?

13

我正在阅读一个包含 unsigned char 变量的 .cpp 文件,它正在尝试进行 16 位的按位左移操作。由于一个 unsigned char 由 8 个 bit 组成,左移 16 位将擦除所有 bit 并用八个0填充。

unsigned char byte=0xff; byte << 16;


4
该表达式的值未被赋值。 - user180326
2
编译器会将移位操作优化为无操作,这是原始代码是什么? - Jonathan Leffler
1
你可能想看一下这个链接:https://dev59.com/oXRC5IYBdhLWcg3wAcXU - AndersK
1
所有内置运算符都适用于至少 int 大小的对象(请参见 https://dev59.com/n2035IYBdhLWcg3wMNGW#5563131)。因此:`sizeof(byte << 16) == sizeof(int)`。 - Martin York
2个回答

18

当你进行值的移位时,

unsigned char x = ...;
int y = x << 16;

如果unsigned char适合于int(大多数系统),则将x的类型提升为int,否则提升为unsigned(罕见情况1)。只要你的int宽度至少为25位,那么就不会丢失任何数据2。

请注意,这与数字16的类型为int完全无关。

/* All three are exactly equivalent */
x << 16;
x << 16u;
x << (unsigned char) 16;

来源:来自n1516(C99草案)

§6.5.7第3段:位移操作符

对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。

§6.3.1.1第2段:布尔值、字符和整数

如果int可以表示原始类型的所有值(由位域限制),则该值转换为int;否则,它将被转换为无符号int。这些被称为整数提升。

脚注:

1:一些DSP芯片以及某些Cray超级计算机众所周知sizeof(char) == sizeof(int)。 这简化了处理器的加载存储单元的设计,但增加了内存消耗。

2:如果左移通过提升到int然后溢出int,那么这是未定义行为(恶魔可能从你的鼻子里飞出来)。相比之下,溢出unsigned总是有明确定义的,因此位移应该通常在unsigned类型上进行。


非常感谢您详细的解释。虽然我仍然对将unsigned char提升为int后可能会意外覆盖的内存感到困惑。 - leomayleomay
1
@leomayleomay:这不是它的工作方式。如果你将一个int赋值给一个unsigned char变量,那么int会被转换。(毕竟这不是汇编语言。) - Dietrich Epp
这里的按位左移(向左移动16位)会使得变量变为0,对吗? - leomayleomay
如果你赋值 x = x << 16,那么在大多数系统上,这等同于 x = 0 - Dietrich Epp
1
我对这种行为感到非常惊讶。在使用IAR CSTAT静态分析工具分析以下代码时,我遇到了这种行为: unsigned short BE2LE16(unsigned short x) { return ((x<<8) | (x>>8));} 直到看到你详细的答案,我才弄清楚它的原因。非常感谢您和IAR! - lkanab

1
如果char适合于int,它将被提升为int,结果将如您所期望的那样。如果不是这种情况,则根据标准,它是未定义的行为,并且可能会发出编译警告。从标准来看: 对每个操作数执行整数提升。结果的类型是提升后左操作数的类型。如果右操作数的值为负或大于或等于提升后左操作数的宽度,则行为是未定义的。

1
如果 char 无法适应于 int,那么它必须适应于 unsigned int,因此不会导致未定义的行为。只有有符号整数的移位是未定义的,而且仅当数学结果不能由该类型表示时才是未定义的。例如,如果 int 是16位,则 1 << 16 是未定义的。 - Dietrich Epp
1
整数提升确实会被执行,这意味着只有当 unsigned char 被提升为 int 而不是 unsigned int 且移位操作将产生一个无法表示为 int 的值时,行为才是未定义的。 - caf
@DietrichEpp:即使int是16位,0 << 16也是未定义的行为,因为这个答案中指出的约束条件。 - caf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接