C - 按位操作

A. *_*nis 0 c bit-manipulation bit-shift bitwise-operators

我对C很新,我试图理解CI中的按位运算符在我面前找到这个代码(将2转换为37)

int main(void)
{
    int x = 2;
    x = (x<<x<<x) | (x<<x<<x) | (x << !!x) | !!x ;
    printf("%d\n" , x );  // prints 37 
}
Run Code Online (Sandbox Code Playgroud)

现在这是我第一次看到这样的东西 (x<<x<<x),我不明白它在做什么.任何人都可以详细解释代码中的第二行吗?

Ily*_*lya 9

我建议你将这条长线分成几个小块(有时尝试这样的东西更有效;即仅仅阅读文档是不够的):

int x = 2;
printf("%d\n", x); // prints 2
printf("%d\n", x << x); // prints 8
printf("%d\n", x << x << x); // prints 32
printf("%d\n", !!x); // prints 1
printf("%d\n", x << !!x); // prints 4

printf("%d\n", x); // prints 2 (just to become sure that x was not changed)
Run Code Online (Sandbox Code Playgroud)

所以,你知道初始长线等于x = (32 | 32 | 1 | 4).但这是32 + 4 + 1 = 37.

让我们详细看看:

什么是<<和>>?

移位运算符按位向右移位右边的位数:

  • << 向左移动并在右端添加零.
  • >> 向右移位并添加0,如果值是无符号类型,或者如果它是带符号类型则扩展顶部位(以保留符号).

此外,C标准还说E1 >> E2:" 如果E1有一个带符号的类型和一个负值,则结果值是实现定义的. "算术移位不能保证.

由于<<是左关联的,因此x<<x<<x被评估为(x<<x)<<x.

另请参阅:什么是按位移位(位移)运算符以及它们如何工作?

什么是!!x?

这是一个一元的NOT和一个一元的NOT. !!x可以用来作为速记(x != 0 ? 1 : 0).

  • 我认为值得指出的是,因为<<是左关联的,x << x << x被评估为(x << x)<< x (6认同)
  • C标准说'E1 >> E2`:"*如果`E1`有一个带符号的类型和一个负值,结果值是实现定义的.*"不保证算术移位. (4认同)
  • @NoamD有 - [`!0 == 1`](/sf/ask/556216531/) (2认同)

Lun*_*din 5

这被称为“混淆”:编写不必要的复杂代码,以使某些东西看起来更高级。

查看子表达式x<<x<<x,它是简单的逻辑左移。移位运算符的运算符结合性是从左到右的,因此表达式等于(x<<x)<<x。

我们将 2 左移 2 次得到 8. 8 << 2,左移 8 次 2 得到 32:

x = 32 | 32 | (x << !!x) | !!x ;
Run Code Online (Sandbox Code Playgroud)

然后对于任何32 | 32操作数32相同的按位 OR 的表达式,与32不使用 OR 的写作完全相同。所以它等价于:

x = 32 | (x << !!x) | !!x ;
Run Code Online (Sandbox Code Playgroud)

!!是在 C 中将任何整数转换为布尔值 0 或 1 的一个有点常见但晦涩的技巧。!!不是一个运算符,而是逻辑 not 运算符的两倍!。首先我们有!2哪个是0。那么!0这给1. 我们只剩下这个:

x = 32 | (2 << 1) | 1;
Run Code Online (Sandbox Code Playgroud)

2 << 1 是 4,所以:

x = 32 | 4 | 1;
Run Code Online (Sandbox Code Playgroud)

写成二进制:

   0010 0000
OR 0000 0100
OR 0000 0001
------------
   0010 0101 = 0x25 hex = 37 dec
Run Code Online (Sandbox Code Playgroud)