【问题标题】:Modify higher bits of a variable in C修改C中变量的高位
【发布时间】:2022-07-14 04:05:17
【问题描述】:

我在 C 中有一个变量,其二进制值为 \'10010100\' 我还有另一个变量,其值为\'1111\'。 我想要实现的是保持位 7,6,1,0 完整,并在 [5..2] 中插入第二个变量。

有人告诉我可以使用镜子.我做了一些研究,我找不到正确的答案。

如果我按位移动,我会丢失部分内容。

  • v1 = (v1 & ~0x3C) | ((v2 & 0xF) << 2);先将四位归零,然后隔离新的四位,对齐合并。
  • 问:有人告诉我我可以使用镜子。答:你应该已被告知使用“位掩码”、应用布尔运算符(“和”、“或”适当的位)和移位运算符(定位位)。请参阅下面 MikeCAT 的回复。如果您愿意,请务必“赞成”和“接受”。

标签: c bitwise-operators bit-shift


【解决方案1】:
  1. 使用掩码(按位与)将应替换为新值的部分归零。
  2. 使用按位或将新值放入归零区域。
    int a = 0x94; /* 10010100 */
    int b = 0xf;  /* 1111     */
    
    /*       do masking here                        */
    /*       |               put the new value here */
    /*       |               |                      */
    /*       v               v                      */
    a = (a & ~(0xf << 2)) | (b << 2);
    

【讨论】:

    【解决方案2】:

    此问题的一般解决方案是使用&amp; 运算符和适当的掩码清除目标范围中的位,并设置来自第二个变量的位,如果不能断言没有设置其他位,则适当移位并可能被掩码:

    v1 = (v1 & ~(0xF << 2)) | ((v2 & 0xF) << 2);
    

    如果您知道v2 的所有位都设置在目标范围内,则可以简化为:

    v1 = v1 | (0xF << 2);
    

    但是请注意(v1 &amp; ~(0xF &lt;&lt; 2)) 使用int 算术,如果掩码类型大于int,它将扩展掩码到v1 的宽度。但是,如果目标包含int 类型的符号位,则将1 位移动到该位置是未定义的行为。使用显式常量也不行,因为它的类型为unsigned int,如果v1 的类型大于int,则将其扩展为v1 的类型也会掩盖v1 的高位。例如:

    /* replacing bits 31,30,29,28 */
    long long v1 = 0x987654321;
    int v2 = 0xF;
    v1 = (v1 & ~(0xF << 28)) | ((v2 & 0xF) << 28);
    // v1 is now 0x9F7654321 but really undefined behavior
    v1 = (v1 & ~0xF0000000) | ((v2 & 0xF) << 28);
    // v1 is now 0xF7654321 instead of 0x9F7654321
    

    如果v2 的类型小于v1 的类型并且必须移动到其自身长度之外,则会出现类似的问题。

    一种更安全的方法是使用带有与v1 类型匹配的尾随类型标记的常量,但如果必须将一个位移入long long 类型的符号位,这仍然不起作用:

    v1 = (v1 & ~(0xFLL << 60)) | ((v2 & 0xFLL) << 60); // undefined behavior
    

    一般的解决方案是使用unsigned long long 常量:

    v1 = (v1 & ~(0xFULL << 28)) | ((v2 & 0xFULL) << 28);
    

    过时的非 2 的补码体系结构的行为并非微不足道,将被忽略。

    【讨论】:

    • C2x 要求 2 的补码算法。
    • @JonathanLeffler:终于!因此的话过时的将被忽略。请注意,1 &lt;&lt; 31 仍然是未定义的行为,-1 &gt;&gt; 1 实现已定义。
    • 使用函数,您可以强制执行无符号算术。
    【解决方案3】:

    这是我将其分解成小块的方法。

    请注意,0b 前缀是非标准扩展,但在多个编译器中很常见。

    #include <stdio.h>
    #include <stdint.h>
    
    int main(void) {
        uint8_t a = 0b10010100;
        uint8_t b = 0b00001111;
        
        uint8_t keep7610 = a & 0b11000011;     // Keep Bits 7, 6, 1, and 0. Set the others to 0
        uint8_t insertb = keep7610 | (b << 2); // Add in variable B at position 2-5
        
        printf("Final Answer: 0x%02X\n", insertb);
        
        return 0;
    }
    

    输出

    Success #stdin #stdout 0s 5444KB
    Final Answer: 0xBC
    

    0xBC 翻译为 0b10111100,这是我手动按照您的指示得到的)

    【讨论】:

    • 二进制常量在 C++14 中,并将在 C2x 中。 “数字标点符号”也是如此,它允许您编写 0b1001'0100 并在其中添加一个单引号 — 这使得 C++ 代码的临时解析变得困难(并且也会使 C 代码的临时解析变得困难)。
    【解决方案4】:

    使用函数!

    unsigned replaceBits(unsigned val, int startBit, int nBits, unsigned newVal)
    {
        unsigned mask = ((1UL << nBits) - 1) << startBit;
    
        //clear bits
        val &= ~mask;
    
        //set new value  (adding with mask makes sure that we will not change any other bits.
        val |= (newVal << startBit) & mask;
        
        return val;
    }
    

    您还可以添加一些检查以确保参数具有有效值。

    例子:

    https://godbolt.org/z/zofKqofx1

    【讨论】:

    • 任何评论沉默的DV-ter?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    相关资源
    最近更新 更多