左移溢出
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
是实现定义的。最可能的结果是 104
的 char
值。
编辑
您发布了实际代码:
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
),结果是实现定义的。通常丢弃高阶位,这将产生 0x68
或 104
.
的结果
编译器警告您,您正在将值 1128
转换为 signed char
,该值不足以容纳它。
也许您期望 0x8d
是 signed char
类型?它不是。表达式的类型几乎总是由表达式本身决定,而不是由它出现的上下文决定。整数常量,无论是十进制、十六进制还是八进制,都始终是 int
类型,除非它们超过 INT_MAX
的值,后者至少是 32767
(也可能是 2147483647
). (或者除非他们有像 L
或 U
这样的后缀,但这与这里无关。)
更新:
您向我们展示了您的原始代码:
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
,任何试图使其适合的尝试都必然会导致问题。通过调整代码,您可能设法消除编译器的警告,但您仍在做一些没有意义的事情。
我不认为我完全理解当我左移一个带符号的字符时 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
是实现定义的。最可能的结果是 104
的 char
值。
编辑
您发布了实际代码:
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
),结果是实现定义的。通常丢弃高阶位,这将产生 0x68
或 104
.
编译器警告您,您正在将值 1128
转换为 signed char
,该值不足以容纳它。
也许您期望 0x8d
是 signed char
类型?它不是。表达式的类型几乎总是由表达式本身决定,而不是由它出现的上下文决定。整数常量,无论是十进制、十六进制还是八进制,都始终是 int
类型,除非它们超过 INT_MAX
的值,后者至少是 32767
(也可能是 2147483647
). (或者除非他们有像 L
或 U
这样的后缀,但这与这里无关。)
更新:
您向我们展示了您的原始代码:
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
,任何试图使其适合的尝试都必然会导致问题。通过调整代码,您可能设法消除编译器的警告,但您仍在做一些没有意义的事情。