【问题标题】:can't shift negative numbers to the right in c不能在c中将负数向右移动
【发布时间】:2018-03-21 01:56:32
【问题描述】:

我正在阅读“K&R 的 C 语言”。现在我正在做按位部分。我很难理解以下代码。

int mask = ~0 >> n;

我正在使用它来掩盖另一个像这样的二进制文件的左侧。 0000 1111 1010 0101 //随机数

我的问题是当我打印 var mask 时它仍然是负数 -1。假设 n 为 4。我认为移动 ~0 即 -1 将是 15 (0000 1111)。

感谢您的回答

【问题讨论】:

  • 这本书享有很高的声誉,但它非常古老,在处理现代(2005 年后)C 编译器时会让你误入歧途。
  • @zwol 你推荐什么书?
  • @TheCaptain 我很遗憾地说我没有推荐新书。我自己很久以前就学过 C。

标签: c bit-manipulation


【解决方案1】:

对负值执行右移会产生一个实现定义值。正如您在您的案例中看到的那样,大多数托管实现将在左侧移动 1 位,但不一定是这种情况。

无符号类型以及有符号类型的正值在右移时总是在左移0 位。因此,您可以通过使用无符号值来获得所需的行为:

unsigned int mask = ~0u >> n;

此行为记录在C standard 的第 6.5.7 节中:

5 E1 >> E2的结果是E1个右移E2位位置。如果 E1 有一个无符号类型,或者如果 E1 有一个有符号类型和一个非负数 value,结果的值是商的整数部分 E1 / 2E2 。如果 E1 具有带符号类型和负值,则 结果值是实现定义的。

【讨论】:

  • 更准确地说,它产生一个实现定义的值。 N1570 6.5.7p5。 (实现定义的 behavior 可以允许它做一些除了产生值之外的事情。)
  • 实现定义的值是不可预测的值?
  • @TheCaptain 实现定义意味着 C 标准没有指定值必须是什么,但具体实现(即 gcc、MSVC 等)必须记录它在这种情况下的作用。如果这些值是不可预测的,那将被称为 indeterminateunspecified 值。
【解决方案2】:

右移负符号整数是一种实现定义的行为,通常(但不总是)用 1 而不是 0 填充左边。这就是为什么无论你移动了多少位,它总是 -1,因为左边总是被 1 填充。

当你移动无符号整数时,左边总是被零填充。所以你可以这样做:

unsigned int mask = ~0U >> n;
                      ^

您还应该注意,int 通常为 2 或 4 个字节,这意味着如果您想获得 15,则需要右移 12 或 28 位而不是仅 4 位。您可以使用 char 代替:

unsigned char mask = ~0U;
mask >>= 4;

【讨论】:

  • U 是什么意思,我可以在哪里阅读有关此主题的更多信息?这本书没有给出很好的细节。根本没有U。
  • @TheCaptain:整数常量上的U 后缀意味着它是无符号类型,在这种情况下特别是unsigned int
  • @TheCaptain 我认为C++ Reference 是一个很好的参考网站。查看该链接,您会找到有关班次运算符的详细说明。
  • 右移一个负符号整数会产生一个实现定义的结果。
  • @KeithThompson 我意识到了这一点。看看现在修改是否有意义。
【解决方案3】:

在 C 和许多其他语言中,>>(通常)在对 signed 变量(如 int)执行时是 arithmetic right shift。这意味着从左边移入的新位是前一个最高有效位 (MSB) 的副本。这具有保留二进制补码负数(在本例中为值)的符号的效果。

这与logical right shift 形成对比,其中 MSB 始终被替换为 0 位。当您的变量是无符号(例如unsigned int)时,这会应用。

来自维基百科:

C 和 C++ 中的 >> 运算符不一定是算术移位。通常,如果在其左侧与有符号整数类型一起使用,它只是算术移位。如果它被用于无符号整数类型,它将是一个逻辑移位。

就您而言,如果您打算在位级别上工作(即使用掩码等),我强烈建议您做两件事:

  1. 使用无符号值。
  2. 使用<stdint.h> 中具有特定大小的类型,例如uint32_t

【讨论】:

  • 你的改变现在有意义了 :-)
  • 右移一个负符号整数会产生一个实现定义的结果。
猜你喜欢
  • 1970-01-01
  • 2022-01-19
  • 2012-04-02
  • 2021-11-23
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多