为什么位移位在C中要这样写?

Why bit shifting is written like this in C?

也许这个问题有点愚蠢,但我想知道为什么 如果我们想在寄存器中的某个特定位置写 0 我们必须这样写:

PORTB &= ~(1 << 2);

而不是这样(因为它不起作用):

PORTB &= (0 << 2);

这里的 0 是不是像 0b00000000(对于 8 位寄存器)一样?

为了简单起见,使用一个字节,0 << 2 正好等于零 - 0b00000000。结果,您与此运算的任何值也将产生零。

1 << 2 等于 0b00000100

~(1 << 2) 等于 0b11111011

现在你有了一个 AND 掩码,它只会将特定位设置为零。其余位将保持不变。

让我们一步步分解:

// PORTB &= ~(1 << 2);

int bit_position = 2;
int bit_mask = 1;              // = 0000 0001
bit_mask <<= bit_position;     // = 0000 0100
int inverted_mask = ~bit_mask; // = 1111 1011

int PORTB = 0x77;              //   0111 0111
PORTB &= inverted_mask;        // & 1111 1011
                               // = 0111 0011

而且,您建议的备选方案:

// PORTB &= (0 << 2);

int bit_position = 2;
int bit_mask = 0;              // = 0000 0000
bit_mask <<= bit_position;     // = 0000 0000

int PORTB = 0x77;              //   0111 0111
PORTB &= bit_mask;             // & 0000 0000
                               // = 0000 0000

重点是计算机只是在整个寄存器上一个一个地执行操作。它不知道它应该关心该数字中所有零中的特定零。所以你不是通过“位置 2 处的零”进行 AND 操作,而只是通过数字零进行操作。事实上,您不是只移动一个零或一个,而是移动整个数字。

这就是为什么要将特定位设置为零,我们必须与掩码进行 AND 操作,该掩码在除我们希望为零的位置之外的每个位置都有一个。 (AND 与 1 不改变位,而 AND 与 0 使其为零。)

你反汇编代码就能明白为什么了:

PORTB &= ~(1 << 2);

是这样翻译的:

PORTB = PORTB & (~(0b00000001 << 2));

PORTB = PORTB & (~(0b00000100));

PORTB = PORTB & ((0b11111011));

这意味着它将采用 PORTB 的旧值并将其第 2 位写入 0,同时将其余位保留为原始值(无论它们是 0 还是 1)

这样做是因为您只想更改 PORTB 的 PIN2 的值而不更改任何其他引脚值。

而另一行:

PORTB &= (0 << 2);

评估为

PORTB = PORTB & ((0b00000000));

这又会将所有 PORTB 引脚重置为 0。

所以这真的取决于你到底想做什么。

再比如你想把PORTB的PIN2设置为1:

PORTB = PORTB | (0b00000100);

可以这样写:

PORTB |= (1 << 2);

这将确保仅将 PIN2 设置为 1,而不会将 PORTB 的其他引脚设置为 1。