左移溢出

Left Shift Overflow

我不认为我完全理解当我左移一个带符号的字符时 c 编译器中发生了什么。我用值 0x8d << 3 初始化它,它给了我一个警告:隐式常量转换溢出。我假设这是因为它为 char 提供了太大的值,但是当我手动完成时,我得到 104 作为答案。

那么这是否意味着当值被移动时它正在做

10001101 << 3 = 10001101000

而不是

10001101 << 3 = 01101000

弄清楚了,但这是代码:

signed char testChar = 0x8d << 3;
printf("testChar : %d\n", testChar);

只需要正常初始化它然后将它转移到另一条线上,超级简单的东西。

你的问题很困惑:你是在用值 0x8d << 3 初始化一个有符号的字符吗?这显然是实现定义,因为该值超出了类型的范围并且该类型已签名。编译器发出警告,因为精度损失是明确的。

相反,如果您的意思是左移一个有符号字符,左移一个值为0x8d的有符号字符,这又是不正确的:除非char 超过 8 位,有符号字符的值不能为 0x8d。它可以具有具有相同位模式的 -115 值。左移将以这种方式计算:

  • char 值首先提升为 int
  • int 值左移 3 但位置:这相当于乘以 8。
  • 如果值为负,标准表示行为未定义,但大多数当前使用 2s 补码表示负值的处理器只会产生与乘以 8 相同的结果。

结果值为 -920。此值不适合 char。将其存储到 char 是实现定义的。最可能的结果是 104char 值。

编辑

您发布了实际代码:

signed char testChar = 0x8d << 3;
printf("testChar : %d\n", testChar);

如果 char 是 8 位,赋值具有实现定义的行为,因为 1128 超出了类型 signed char (-128..127) 的范围,大多数情况下的行为现代处理器是屏蔽掉高位并将 8 个低位存储到目标字节中,就好像它是一个 unsigned char。在你的例子中,结果是 1128 & 255 -> 104.

您没有显示您的实际代码(见下文),但您提供了足够的信息来推断它:

signed char c = 0x8d << 3;

常量 0x8d 的类型为 int。将其左移 3 位产生值 0x468,或等效地 1128.

初始化隐式将该值从 int 转换为 signed char。由于该值超出了 signed char 的范围(除非您使用的是非常奇怪的系统,否则它的范围大概是 -128+127),结果是实现定义的。通常丢弃高阶位,这将产生 0x68104.

的结果

编译器警告您,您正在将值 1128 转换为 signed char,该值不足以容纳它。

也许您期望 0x8dsigned char 类型?它不是。表达式的类型几乎总是由表达式本身决定,而不是由它出现的上下文决定。整数常量,无论是十进制、十六进制还是八进制,都始终是 int 类型,除非它们超过 INT_MAX 的值,后者至少是 32767(也可能是 2147483647 ). (或者除非他们有像 LU 这样的后缀,但这与这里无关。)

更新:

您向我们展示了您的原始代码:

signed char testChar = 0x8d << 3;
printf("testChar : %d\n", testChar);

我希望这会产生警告,因为您用来初始化 testChar 的值 1128 会导致溢出。 testChar 中存储的值是实现定义的,但很可能是 104,丢弃高位的结果。

如果将班次拆分为单独的作业:

signed char testChar = 0x8d;
testChar = testChar << 3;

那么我希望得到相同的结果。但是值 0x8d 太大而无法存储在 signed char 中,因此转换会产生一个实现定义的结果——很可能是 -115。负值的左移具有未定义的行为——但同样,它可能会产生 -920,当转换为 signed char 时可能会得到 104.

最终,您的问题的解决方案是做一些与您尝试做的事情不同的事情。值 0x8d << 3 不适合 signed char,任何试图使其适合的尝试都必然会导致问题。通过调整代码,您可能设法消除编译器的警告,但您仍在做一些没有意义的事情。