为什么一个变量和一个数字的移位没有相同的结果?
Why doesn't the bit-shift of a variable and a number have the same result?
我正在移动一些位,然后才意识到使用变量进行运算的结果与使用数字的结果不同。请参阅下面的示例。
int a = 97;
int b = 0;
b = 1 << a;
printf("%d\n", b);
// 2
b = 1 << 97;
printf("%d\n", b);
// 0 - warning: shift count >= width of type [-Wshift-count-overflow]
您看到的警告是编译时警告。现在,你可以清楚地看到你的 int b
是一个 32 位变量,如果 left-shifted 97 次就会溢出。所以,这是一个合理的担忧。但是编译器只能检测到这种溢出,因为它是在编译期间评估的,并且编译器立即知道它会溢出。
在移位次数可变的情况下,编译器不够智能,无法知道 int a
归结为移位时将拥有什么值。因此,编译器将其留给您。
由于 left shift 的右操作数大于左操作数的位长度的结果未定义,因此表达式的任何结果都是可能的。
在可变情况下(1 << a
),由于a
是97(大于int
中的位数),最可能的结果是1 << (97 % 32)
== 1 << 1
== 2
或 0
,通常取决于硬件 (CPU) 处理这些转变的方式。
使用常量 (1 << 97
),编译器知道你移动得太远,发出警告(这不是必需的),并将结果定义为 0
(也不是必需的) ).
这里的 C++ 标准中概述了未定义的行为。
http://eel.is/c++draft/expr.shift
The behavior is undefined if the right operand is negative, or greater
than or equal to the width of the promoted left operand.
根据编译器和优化级别,您会得到不同的结果。如果打开优化,编译器会很容易地优化掉第一个移位操作,然后将其也设为 0。
为什么它会那样做呢?用于按变量移位的 x86 指令是 SAL (shift-arithmetic-left)。您可以在此处查看移位操作的指令列表:
https://c9x.me/x86/html/file_module_x86_id_285.html
将在未优化的构建中使用的是 SAL r/m32, CL
。 CL
寄存器是 8 位,但处理器在内部将其屏蔽为 5 位:
The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to 5 bits, which limits the count range to 0 to 31. A special opcode encoding is provided for a count of 1.
我正在移动一些位,然后才意识到使用变量进行运算的结果与使用数字的结果不同。请参阅下面的示例。
int a = 97;
int b = 0;
b = 1 << a;
printf("%d\n", b);
// 2
b = 1 << 97;
printf("%d\n", b);
// 0 - warning: shift count >= width of type [-Wshift-count-overflow]
您看到的警告是编译时警告。现在,你可以清楚地看到你的 int b
是一个 32 位变量,如果 left-shifted 97 次就会溢出。所以,这是一个合理的担忧。但是编译器只能检测到这种溢出,因为它是在编译期间评估的,并且编译器立即知道它会溢出。
在移位次数可变的情况下,编译器不够智能,无法知道 int a
归结为移位时将拥有什么值。因此,编译器将其留给您。
由于 left shift 的右操作数大于左操作数的位长度的结果未定义,因此表达式的任何结果都是可能的。
在可变情况下(1 << a
),由于a
是97(大于int
中的位数),最可能的结果是1 << (97 % 32)
== 1 << 1
== 2
或 0
,通常取决于硬件 (CPU) 处理这些转变的方式。
使用常量 (1 << 97
),编译器知道你移动得太远,发出警告(这不是必需的),并将结果定义为 0
(也不是必需的) ).
这里的 C++ 标准中概述了未定义的行为。
http://eel.is/c++draft/expr.shift
The behavior is undefined if the right operand is negative, or greater than or equal to the width of the promoted left operand.
根据编译器和优化级别,您会得到不同的结果。如果打开优化,编译器会很容易地优化掉第一个移位操作,然后将其也设为 0。
为什么它会那样做呢?用于按变量移位的 x86 指令是 SAL (shift-arithmetic-left)。您可以在此处查看移位操作的指令列表:
https://c9x.me/x86/html/file_module_x86_id_285.html
将在未优化的构建中使用的是 SAL r/m32, CL
。 CL
寄存器是 8 位,但处理器在内部将其屏蔽为 5 位:
The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to 5 bits, which limits the count range to 0 to 31. A special opcode encoding is provided for a count of 1.