移位取消而不是预期的行为
Bitshifts canceling out instead of expected behavior
我注意到带括号的位移位有一些奇怪的行为
#include <stdio.h>
int main(void)
{
unsigned char c;
unsigned char d;
c = 153;
c = (c << 7) >> 7;
printf("%d\n", c);
d = 153;
d = (d << 7);
d = (d >> 7);
printf("%d\n", d);
}
输出:
153
1
我预计 c
的值也为 1...发生了什么事?这是未定义的吗?
位移 char
会自动将其提升为 int
。这就是为什么左移 7 位不会砍掉任何东西的原因。
资料来源:the C standard
的第 6.5.7 节
The integer promotions are performed on each of the operands. The type of the result is
that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
它被评估为 int
,然后存储为 char
。并且在中间步骤中不是 char
。换句话说,当您对 char
进行位移时,它会提升为 int
.
查看标准 6.5.7 Bitwise shift operators 的内容:
The integer promotions are performed on each of the operands.
The type of the result is that of the promoted left operand.
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is
undefined.
根据 的建议,我们看一下在 x86 平台上为表达式 c = (c << 7) >> 7
:
生成的代码
movzbl 31(%esp), %eax ;eax = c
sall , %eax ;left shift
sarl , %eax ;right shift
movb %al, 31(%esp) ;c = al (one byte)
c
的内容被加载到一个32位寄存器(eax
),并且两个移位都在这个寄存器上执行。最后,将该寄存器的最低有效字节(即al
)赋给变量c
.
简而言之,两个移位都被评估为操作数是 32 位宽。
根据this online c standard draft,移位运算符将参数提升为整数类型:
6.5.7 Bitwise shift operators
2 Each of the operands shall have integer type.
3 The integer promotions are performed on each of the operands.
所以当你写c = (c << 7) >> 7
时,表达式(c << 7)
中的c
的值先转换为整数值,然后移位。因此,none 位丢失。将它们移回 >> 7
给出原始值。
相反,当您写入 d = (d << 7)
时,一旦将(积分)结果重新分配回 d
,这些位就会丢失,这是一个无符号字符,因此无法保存 "higher" 位整数值。
153
二进制表示为10011001
。位移运算符的操作数类型应为int
,否则将进行整数提升。
对于语句
c = (c << 7) >> 7;
c
被提升为整数并假设整数表示为 4 个字节,c
将是二进制的 00000000 00000000 00000000 10011001
。所以表达式
c = (c << 7) >> 7; // Left and right shift operator will nullify the effect of each other.
将具有表达式
的效果
c = c;
如果
d = (d << 7);
d = (d >> 7);
在第一条语句后 d
将具有值(二进制)10000000
。在第二条语句后 d
将具有值(二进制)00000001
.
我注意到带括号的位移位有一些奇怪的行为
#include <stdio.h>
int main(void)
{
unsigned char c;
unsigned char d;
c = 153;
c = (c << 7) >> 7;
printf("%d\n", c);
d = 153;
d = (d << 7);
d = (d >> 7);
printf("%d\n", d);
}
输出:
153
1
我预计 c
的值也为 1...发生了什么事?这是未定义的吗?
位移 char
会自动将其提升为 int
。这就是为什么左移 7 位不会砍掉任何东西的原因。
资料来源:the C standard
的第 6.5.7 节The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
它被评估为 int
,然后存储为 char
。并且在中间步骤中不是 char
。换句话说,当您对 char
进行位移时,它会提升为 int
.
查看标准 6.5.7 Bitwise shift operators 的内容:
The integer promotions are performed on each of the operands.
The type of the result is that of the promoted left operand.
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
根据 c = (c << 7) >> 7
:
movzbl 31(%esp), %eax ;eax = c
sall , %eax ;left shift
sarl , %eax ;right shift
movb %al, 31(%esp) ;c = al (one byte)
c
的内容被加载到一个32位寄存器(eax
),并且两个移位都在这个寄存器上执行。最后,将该寄存器的最低有效字节(即al
)赋给变量c
.
简而言之,两个移位都被评估为操作数是 32 位宽。
根据this online c standard draft,移位运算符将参数提升为整数类型:
6.5.7 Bitwise shift operators
2 Each of the operands shall have integer type.
3 The integer promotions are performed on each of the operands.
所以当你写c = (c << 7) >> 7
时,表达式(c << 7)
中的c
的值先转换为整数值,然后移位。因此,none 位丢失。将它们移回 >> 7
给出原始值。
相反,当您写入 d = (d << 7)
时,一旦将(积分)结果重新分配回 d
,这些位就会丢失,这是一个无符号字符,因此无法保存 "higher" 位整数值。
153
二进制表示为10011001
。位移运算符的操作数类型应为int
,否则将进行整数提升。
对于语句
c = (c << 7) >> 7;
c
被提升为整数并假设整数表示为 4 个字节,c
将是二进制的 00000000 00000000 00000000 10011001
。所以表达式
c = (c << 7) >> 7; // Left and right shift operator will nullify the effect of each other.
将具有表达式
的效果c = c;
如果
d = (d << 7);
d = (d >> 7);
在第一条语句后 d
将具有值(二进制)10000000
。在第二条语句后 d
将具有值(二进制)00000001
.