【问题标题】:Converting a set of booleans to a number将一组布尔值转换为数字
【发布时间】:2014-02-16 00:53:43
【问题描述】:

这是我将用来获取一组三个布尔值并将其转换为 int 以用于 switch 语句的代码:

int bits = 0;
bool a = true, b = false, c = true;  // 101 = 5

bits = bits | a << 2;
bits = bits | b << 1;
bits = bits | c;

cout << bits;

根据这三个布尔值的组合状态,我有八种情况。我这样做对吗?

是的,不是语法意义上的,但如果有任何问题,请提出建议。更准确地说是“这是解决这个问题的最佳方法吗?”

【问题讨论】:

  • 你是指 C 还是 C++?在大多数情况下,它会有所不同。
  • 在处理多个运算符时,为了优先级清晰,我总是添加括号:bits = bits | (a &lt;&lt; 2); 也就是说,我不确定在 C++ 中如何处理 bool 对象的移位。可能需要将ab 转换为(int)

标签: c++ bit-manipulation


【解决方案1】:

如果你使用 C++,你可以使用bitset&lt;N&gt;

【讨论】:

    【解决方案2】:

    你做得对。不过,您可以使代码更简洁:

    bits |= (a<<2) | (b<<1) | (c<<0);
    

    请注意,标准不会对bool 强制执行任何大小限制。务实地说,三位应该没有问题,但标准在这里并不支持你。

    【讨论】:

    • 为什么bool 的大小限制很重要? b &lt;&lt; i 应该是一个整数。
    • b &lt;&lt; i 产生左操作数类型的整数类型。见 5.8/1。
    【解决方案3】:

    $ 猫 ttt.c

    //example of C solution
    #include <stdio.h>
    
    int main() {
            union {
                    unsigned int val;
                    struct {
                            unsigned a : 1;
                            unsigned b : 1;
                            unsigned c : 1;
                            //unsigned d : 1; 
                            //e, f, g, h...
                    } flags;
            } bits;
    
            bits.val=0;
            bits.flags.a = 1;
            bits.flags.c = 1;
    
            printf("val: %d\n",bits.val);
            return 0;
    }
    

    ~$ ./ttt

    val: 5
    

    【讨论】:

    • 从技术上讲,写入 union 的一个值然后读取另一个值是调用未定义的行为。
    • 太棒了!我忘记了联合是如何使位操作如此简单的!只是一个问题,与“定义”选项相比,联合是否增加了代码大小?
    • 我也成为了工会的牺牲品。在苏联,他们说“你不使用联盟;联盟使用你”。为了提高效率,C++ 标准允许编译器实现者按照他们认为合适的方式对齐结构和联合。这是以允许我们傻瓜依赖位于内存中特定地址的成员为代价的。正如@sbi 正确指出的那样,这在工会的情况下意味着您不能写入一个成员然后从另一个成员读取。您写入的成员的起始地址可能不是您所期望的。唉,这个程序不正确。
    • @sbi, wilhelmtell - 那么,如果不能保证成员的对齐,那么工会有什么意义呢?如果你不能给一个成员写信,从另一个成员那里读?我记得以前使用联合的一个例子是寄存器和半寄存器(union REGS)。我们有一个有 2 个成员的联合,一个是一个寄存器,另一个是两个半寄存器的结构。然后,您可以使用这种类型的对象方便地与系统调用进行交互。但是,这是 C。这是 C 和 C++ 之间的不同吗?
    • 我不记得了(阅读:我现在懒得去谷歌搜索)C 标准是怎么说的。但我相当有信心这是 C 和 C++ 共有的东西。联合的想法是在两个或多个数据之间共享内存,当您知道您永远不需要它们时。您不能同时将联合用于两个不同的任务。当您使用工会成员时,您必须假设其他成员不存在并且从未存在过。换句话说,当你写入一个成员时,不要从另一个成员中读取——它不存在。当您写入另一个时,不要从第一个读取。
    【解决方案4】:

    我会这样做:

    bits = (bits << 1) | a;
    bits = (bits << 1) | b;
    bits = (bits << 1) | c;
    

    如果您需要添加或删除标志,则需要较少的维护工作。

    但是,这样做以便您可以将其用于switch 听起来可能不是一个好主意。添加标志会使您需要处理的状态数量增加一倍,而case 值会很脆弱且难以阅读。

    但如果你真的需要,这里有另一种方法:

    enum
    {
        c_bit_offset,
        b_bit_offset,
        a_bit_offset
    };
    
    unsigned int bits =   (a << a_bit_offset)
                        | (b << b_bit_offset)
                        | (c << c_bit_offset);
    switch (bits)
    {
        case 0:
           /* Do something. */
           break;
        case (1 << a_bit_offset):
           /* Do something. */
           break;
        case (1 << a_bit_offset) | (1 << b_bit_offset):
           /* Do something. */
           break;
        ...
    }
    

    顺便说一句,您可能应该使用unsigned int 而不是int

    【讨论】:

      【解决方案5】:

      您可以进行一些定义以方便使用位

      #define BitSet(arg,bit) ((arg) |= (1<<bit))
      #define BitClr(arg,bit) ((arg) &= ~(1<<bit)) 
      #define BitFlp(arg,bit) ((arg) ^= (1<<bit)) 
      #define BitTst(arg,bit) ((arg) & (1<<bit)) 
      

      那么你只能使用一个字符

      keys = 0b00000101;
      BitSet (keys,1);
      

      这是在嵌入式系统中工作的常见方式。

      【讨论】:

        【解决方案6】:

        您始终可以显式进行转换:

        bits = bits | (a ? 1 : 0) << 2;
        

        但我相信 C/C++ 会在您使用位移运算符后隐式处理它。

        您应该为每个标志定义常量,为您设置的位命名:

        const int a_flag = 2;
        bits = bits | (a ? 1 : 0) << a_flag;
        

        编辑:

        从我回答的 cmets 中可以看出,大多数 C/C++ 程序员更喜欢学习隐式转换和运算符优先级。因此,您的原始代码是“最正确的”。

        编辑 2:

        除了,您应该使用|= 运算符:

        const int a_flag = 2;
        bits |= a << a_flag;
        

        【讨论】:

        • 在 C++ 中,bool 是一个整数类型,其值为 0 或 1,因此显式是没有意义的。 (并且使用三元运算符会破坏使用switch 而不是一系列if 语句的意义。)
        • (a ? 1 : 0) 表示(a != 0)
        • @wilhelmtell:它的“意思”就是代码所说的。意义是根据人类阅读的内容来定义的。它在逻辑上可以替代的东西并不能真正描述程序员的意图。此外,代码编译成的内容是另一回事 :) 编译后的代码没有布尔值的概念,只有受约束的整数值。
        • @jamesdlin:编译器优化除外。我不知道常见的编译器优化是什么,但这些应该是一个明显的例子,并且最终成为相同的代码。与 some_integer_variable / 2 之类的东西相同,被转换为位移位。
        • @wilhelmtell:但是,我同意大多数 C/C++ 程序员更喜欢编码器来理解隐式转换和运算符优先级,因此会认为他的原始代码更接近正确的风格。
        猜你喜欢
        • 1970-01-01
        • 2014-08-10
        • 2016-01-13
        • 1970-01-01
        • 1970-01-01
        • 2015-02-23
        • 2019-05-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多