【问题标题】:Why bit shifting is written like this in C?为什么位移在 C 中是这样写的?
【发布时间】:2021-10-23 11:18:05
【问题描述】:

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

PORTB &= ~(1 << 2);

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

PORTB &= (0 << 2);

这里的 0 是否意味着像 0b00000000(对于 8 位寄存器)?

【问题讨论】:

  • 0 为零,无论您移动多少。计算机不知道您的意思是一个特定的零,它只是将两个整数与。
  • ~(1<<2) 是(8 位二进制)~(0b00000100)0b11111011。当您与该值 AND (&) 时,除位 2 之外的所有位都保持不变

标签: c embedded


【解决方案1】:

让我们逐步分解:

// 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 处的零”进行与运算,而只是通过数字零。确实,您移动的不仅仅是一个零或一,而是整个数字。

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

【讨论】:

  • 作为相关评论,您可能已经注意到在某些 AVR 库中,某些位被定义为 #define DISABLE_X (0 &lt;&lt; 2)#define ENABLE_X (1 &lt;&lt; 2) 对。这只是为了让您可以编写REG = DISABLE_X | ENABLE_Y 之类的内容来明确设置的内容。但是零掩码是完全可选的,即它与写REG = ENABLE_Y完全相同。
【解决方案2】:

为了简单起见,使用一个字节,0 &lt;&lt; 2 正好等于零 - 0b00000000。任何你和它的值也将产生零作为结果。

1 &lt;&lt; 2 等于 0b00000100

~(1 &lt;&lt; 2) 等于 0b11111011

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

【讨论】:

    【解决方案3】:

    你可以理解为什么,当你反汇编代码时:

    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。

    【讨论】:

    • 我假设"bit 2" 是指第三个最低有效位,即您使用的是LSB 0 bit numbering。但是,我怀疑所有读者都不会立即明白这一点。
    • 没错。最低有效位 (LSB) 编号为 0。对于所有 C 编译器都是如此,除非另有配置(如果可能)。
    • 重新。 “适用于所有 C 编译器”,我不知道有任何 C 功能允许您按数字引用单个位。但是,此编号与使用 &lt;&lt; 运算符将 1 移动到所需位置相匹配,即,1 &lt;&lt; 0 将其留在 LSB 位置,1 &lt;&lt; 1 将其移动一。移位行为无法更改/配置,否则对于其他用途(例如使用移位乘以 2 的幂)是错误的。
    猜你喜欢
    • 1970-01-01
    • 2017-10-13
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2017-10-31
    • 1970-01-01
    相关资源
    最近更新 更多