编译器是否优化了净零位移位?

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 ;

这说明了两者之间的有效区别 - 观察到的行为不需要优化,而是需要语言定义关于表达式中的类型提升规则。

也就是说,编译器也完全有可能优化这些移位。在这种情况下,优化可能不会改变 已定义 行为的结果。