这就是 C 和二进制算术的工作原理:
如果你离开 shift 0xff << 3
,你会得到二进制:00000000 11111111 << 3 = 00000111 11111000
如果你右移0xff >> 3
,你会得到二进制:00000000 11111111 >> 3 = 00000000 00011111
0xff
是一个(有符号的)整数,具有正值255
。由于它是正数,因此转换它的结果是 C 和 C++ 中定义明确的行为。它不会做任何算术移位,也不会做任何种类或定义不明确的行为。
#include <stdio.h>
int main()
{
printf("%.4X %d\n", 0xff << 3, 0xff << 3);
printf("%.4X %d\n", 0xff >> 3, 0xff >> 3);
}
输出:
07F8 2040
001F 31
所以你在你的程序中做了一些奇怪的事情,因为它没有按预期工作。也许您正在使用 char 变量或 C++ 字符文字。
来源:ISO 9899:2011 6.5.7。
问题更新后编辑
int number = ~0;
假设二进制补码,给你一个等于 -1 的负数。
number = number << 4;
调用未定义的行为,因为您左移了一个负数。该程序正确地实现了未定义的行为,因为它要么做某事,要么什么都不做。它可能会打印 fffffff0 或者可能会打印粉红色的大象,或者它可能会格式化硬盘驱动器。
number = number >> 4;
调用实现定义的行为。在您的情况下,您的编译器会保留符号位。这称为算术移位,算术右移以这样一种方式工作,即 MSB 用移位前的任何位值填充。因此,如果您有一个负数,您将体验到该程序正在“移入”。
在现实世界中 99% 的情况下,对有符号数使用位运算符是没有意义的。因此,请始终确保您使用的是无符号数,并且 C/C++ 中没有任何危险的隐式转换规则将它们转换为有符号数(有关危险转换的更多信息,请参阅“整数提升规则”和“通常的算术转换”,关于 SO 的大量好信息)。
编辑 2,来自 C99 标准的基本原理文档 V5.10 的一些信息:
6.5.7 移位运算符
K&R 中移位运算符的描述表明,移位长计数应该迫使左操作数在移位之前扩大到长。C89 委员会认可的一种更直观的做法是,移位计数的类型与结果的类型无关。
C89 中的安静变化
移位长计数不再将移位的操作数强制为长。C89 委员会肯定了 K&R 授予的实现自由,不需要符号右移操作来符号扩展,因为这样的要求可能会减慢快速代码,并且符号扩展移位的有用性是微不足道的。(将负的二进制补码整数算术右移一位与除以二不同!)