编译器是否优化了净零位移位?
Do compilers optimize out net zero bit shifts?
我在 .cpp
文件中有一些代码,例如下面的代码块(我不允许 post 原始代码),我认为它是由 clang++
编译的( Ubuntu clang version 3.5.2-3ubuntu1 (tags/RELEASE_352/final) (based on LLVM 3.5.2)
).
它看起来像 C
代码,因为我们正在使用 GoogleTest
来测试我们的 C
代码。不管怎样:
size_t const SHIFT = 4;
uint8_t var, var2;
/* Omitted: Code that sets var to, say 00011000 (base 2) */
var2 = var;
var = var << SHIFT >> SHIFT; // [1] result is 00011000 (base 2) (tested with printf)
var2 = var2 << SHIFT;
var2 = var2 >> SHIFT; // [2] result is 00001000 (base 2) (tested with printf)
现在,为什么评论 [1]
成立?我假设相应的行会导致前 4 位被清零。但我发现这不是真的;该程序只是恢复原始值。
Is this some language defined behavior, or is clang
compiling out a supposedly useless bit shift?
(我检查了结合性(使用 this table on cppreference.com,假设 associativity/precedence 的基本运算符在 C++
的版本之间没有区别,并且可能在 C++
和 C
要么,至少在 'current versions' 中没有)并且 [1]
处的 RHS 表达式似乎应该产生与以下两个语句相同的结果)
您看到的是整数提升的结果。任何类型低于 int
的值在表达式中使用时,都会提升为 int
.
这在 C standard
的第 6.3.1.1 节中有详细说明
2 The following may be used in an expression wherever an int
or unsigned int
may
be used:
- An object or expression with an integer type (other than
int
or unsigned int
)
whose integer conversion rank is less than or equal to the rank of int
and
unsigned int
.
- A bit-field of type
_Bool
, int
, signed int
, or unsigned int
.
If an int
can represent all values of the original type (as restricted by the width, for a
bit-field), the value is converted to an int
; otherwise, it is converted to an unsigned int
. These are called the integer promotions. All other types are unchanged by the
integer promotions.
在这种情况下:
var = var << SHIFT >> SHIFT;
var
先升为int
。这种类型至少有 16 位宽,最有可能是 32 位宽。所以被操作的值是0x00000018
。左移 4 导致 0x00000180
,随后右移导致 0x00000018
.
然后将结果存储在 uint_8
中。由于该值适合此类型的变量,因此不需要转换并存储 0x18
。
表达式:
var = var << SHIFT >> SHIFT;
在语义上不等同于
var = var << SHIFT ;
var = var >> SHIFT ;
那需要:
var = (uint8_t)(var << SHIFT) >> SHIFT ;
这说明了两者之间的有效区别 - 观察到的行为不需要优化,而是需要语言定义关于表达式中的类型提升规则。
也就是说,编译器也完全有可能优化这些移位。在这种情况下,优化可能不会改变 已定义 行为的结果。
我在 .cpp
文件中有一些代码,例如下面的代码块(我不允许 post 原始代码),我认为它是由 clang++
编译的( Ubuntu clang version 3.5.2-3ubuntu1 (tags/RELEASE_352/final) (based on LLVM 3.5.2)
).
它看起来像 C
代码,因为我们正在使用 GoogleTest
来测试我们的 C
代码。不管怎样:
size_t const SHIFT = 4;
uint8_t var, var2;
/* Omitted: Code that sets var to, say 00011000 (base 2) */
var2 = var;
var = var << SHIFT >> SHIFT; // [1] result is 00011000 (base 2) (tested with printf)
var2 = var2 << SHIFT;
var2 = var2 >> SHIFT; // [2] result is 00001000 (base 2) (tested with printf)
现在,为什么评论 [1]
成立?我假设相应的行会导致前 4 位被清零。但我发现这不是真的;该程序只是恢复原始值。
Is this some language defined behavior, or is
clang
compiling out a supposedly useless bit shift?
(我检查了结合性(使用 this table on cppreference.com,假设 associativity/precedence 的基本运算符在 C++
的版本之间没有区别,并且可能在 C++
和 C
要么,至少在 'current versions' 中没有)并且 [1]
处的 RHS 表达式似乎应该产生与以下两个语句相同的结果)
您看到的是整数提升的结果。任何类型低于 int
的值在表达式中使用时,都会提升为 int
.
这在 C standard
的第 6.3.1.1 节中有详细说明2 The following may be used in an expression wherever an
int
orunsigned int
may be used:
- An object or expression with an integer type (other than
int
orunsigned int
) whose integer conversion rank is less than or equal to the rank ofint
andunsigned int
.- A bit-field of type
_Bool
,int
,signed int
, orunsigned int
.If an
int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint
; otherwise, it is converted to anunsigned int
. These are called the integer promotions. All other types are unchanged by the integer promotions.
在这种情况下:
var = var << SHIFT >> SHIFT;
var
先升为int
。这种类型至少有 16 位宽,最有可能是 32 位宽。所以被操作的值是0x00000018
。左移 4 导致 0x00000180
,随后右移导致 0x00000018
.
然后将结果存储在 uint_8
中。由于该值适合此类型的变量,因此不需要转换并存储 0x18
。
表达式:
var = var << SHIFT >> SHIFT;
在语义上不等同于
var = var << SHIFT ;
var = var >> SHIFT ;
那需要:
var = (uint8_t)(var << SHIFT) >> SHIFT ;
这说明了两者之间的有效区别 - 观察到的行为不需要优化,而是需要语言定义关于表达式中的类型提升规则。
也就是说,编译器也完全有可能优化这些移位。在这种情况下,优化可能不会改变 已定义 行为的结果。