位运算和枚举

作者: FindCrt | 来源:发表于2016-03-16 23:38 被阅读1890次

    我看iOS本身定义的枚举里面经常会使用左移(<<)来定义枚举的值,一开始我还不懂为啥要这么定义。这么处理的逻辑跟iOS系统没关系。

    1、举个例子

    定义:

    typedef enum{
        a = 1 << 0,
        b = 1 << 1,
        c = 1 << 2,
        d = 1 << 3
    }testEnum;
    

    使用:

    testEnum e = a | b;
        
        if (e & a) {
            printf("满足条件a");
            //满足a要做的事
        }
        if (e & b) {
            printf("满足条件b");
            //满足b要做的事
        }
        if (e & c) {
            printf("满足条件c");
            //满足c要做的事
        }
    

    为什么枚举值定义成1左移n位的形式呢?看枚举值的二进制形式:

    • 1 << 0 是00000001
    • 1 << 1 是00000010
    • 1 << 2 是00000100

    规律就是只有一个位上为1,但其他为都为0.这样 e = a | b,二进制形式就是00000011,然后e & b的时候,因为位与(&)的性质,只有都为1才会是1,这样e & a和e & b都会有值,不是0,也就为true。

    用移位来定义枚举就是为了把1的位置错开,然后当你需要同时满足多个枚举值的时候,可以使用位或(|)操作把多个枚举值合并,而不会互相影响。比如 00010000 和 00100000合并,他们的1位置是错开的,合并之后1的位置都保留下来了,变成00110000. 然后使用位与(&)来检测某个位上的1,因为每个枚举值只有一个位上是1,除非你的位上也是1,否则位与操作后就为0了。比如0010000和00010000位与就为0;而00100000和00110000位与就不是0。而前面位或操作又可以把每个枚举值的1都保留了,所以后面位与操作会把它包含的每个枚举值都体现出来。

    也就是如果e = a| b | c | d,那么e & a 、e & b 、e & c 、 e & d都为true.就是你这个枚举值包含了那些原始枚举值,&操作值都为true.这样代码写起来,逻辑就符合人的思维了。

    不知道这个是不是常识,我大学不是计算机专业,也没人跟我专门讲过这个。

    2、引申一下

    上面是使用了2进制来错开,保留每个位,其实其他进制也可以,但位数是2的n次方。
    比如0000 0000 8个位,可以前4个位存储一个值,后4个位存储一个值:

    typedef enum{
        a = 0 << 0,
        b = 1 << 0,
        c = 2 << 0,
        d = 3 << 0,
        
        e = 0 << 4,
        f = 1 << 4,
        g = 2 << 4,
        h = 3 << 4
    }testEnum;
    

    这里的话,a b c d的前4为都是0,值的变化在后4位,而e f g h正好相反。如果你使用 a b c d内的值位或操作,是没法保存两者的,比如一个数是0000 0011,它可以是d,也可以是d | b,没法判断是否含有枚举b,因为1和3的最后一位都是1,一个数末位是1,你不知道这个1是从哪个枚举值带来的。

    所以这样定义a b c d之间是没法共存的。但是a b c d中任何一个都可以和e f g h中任何一个共存。因为它们值存的位置不一样。

    这种枚举举个例子,比如使用枚举给一个苹果指定类型,a b c d可以是4中不同产地,e f g h 可以是不同的品种,你看产地只能有一个、品种也只能有一个,但是品种和产地是可以共存的。

    3、该怎么定义枚举

    在上面一段的基础上看应用实例,反过来再某个使用环境下怎么定义枚举?我的理解是要分层。

    比如有a b c是不可共存的,那好,把他们定义成0 1 2 3 ,然后它们只会占2个位,因为3最大,是0000 00 11,那么接下来其他的枚举值就可以左移2个位来和他们避开。然后 d e是不可共存的,那么就把d e 定义为 0 << 2和1<< 2。注意:a b c 分成第一组,d e分成第二组的意思,除了组内不可共存,也代表组之间可以共存,这就是我说分层的意思。照着这个逻辑就可以把复杂的共存和不共存的相互关系捋清,然后分别定义枚举。组之间的取值区域不能重叠,组之间可以。

    typedef enum{
        a = 0 << 0,
        b = 1 << 0,
        c = 2 << 0,
        
        d = 0 << 2,
        e = 1 << 2,
        
        f = 0 << 3,
        g = 1 << 3,
        h = 2 << 3
    }testEnum;
    
    4、最后,我觉得这个思想在使用任何数做基数都适用,只是计算机的位操作让2变得特别。

    比如有个物品A有10个不同的属性,每个属性都有7个以内的取值,即有属性a b c d e f g h i j,然后a有5个可能取值,b有4个可能取值,c有7个可能取值,等等。按理说,需要10个变量来保存,但其实可以一个数就搞定,让N = a + b * 7 + c * 7的平方 + d * 7的立方 + ...,反之,知道一个数,把它用7进制表示,从低到高就是a b c d ...的值了。

    相关文章

      网友评论

      • 3631fbf90a44:x<<y 是不是 x乘以2的y次方
        FindCrt:@世界之外的孤独者 是的
      • KeepMoveingOn:1.比如一个数是0000 0011,它可以是d,也可以是d | b-----------应该是c|b
        2.比如有a b c是不可共存的-----------应该是比如有a b c是可共存的
        😄
        FindCrt:@KeepMoveingOn 不定成123定成什么?如果是可共存,就按位定义,就是1<<0, 1<<2,1<<3这种,每个数在二进制时都是只一个位是1,这样才可以不互相干扰,才可以“共存”。不可以共存时,为了压缩空间,就直接使用连续的整数,所以使用123。我看你没看懂这篇文章的意思。
        KeepMoveingOn:@Find1991 比如有a b c是不可共存的,那好,把他们定义成0 1 2 3 这句话前后有歧义吧:grin: 可能会误导读者,既然不可共存为什么还要定义成123呢。同时谢谢博主的解答
        FindCrt:1. 我的意思确实是d|b,0011和0001或操作后也是0011,我后面写的“因为1和3的最后一位都是1,一个数末位是1”,所以当时想的就是这个,不是笔误。
        2. “不可共存”的意思是不能同时选择这两个枚举,可能后面举例0 1 2造成误解,0 1 2这三个数枚举值是或操作不会造成歧义。但我的原意是 组内不可共存,即组内的枚举值同时取会造成歧义。
      • 王家薪:常年的疑惑解开了
      • 萧旭:一直存在的一个问题终于解决了啊!
        谢楼主大恩!
      • 不離:第一部分,应该是if((e&a) == a)吧 ,写的很好,很清晰!
        FindCrt:@不離 谢谢阅读。这里我一开始想的就是if (e & a),其实e & a只会两种情况:0和a,也就是非零时就是a。所以e&a为true和e&a == a是等价的。
      • FindCrt:从新看了下自己写的这篇,思考的还是挺好的,哈哈
        Jack_zz:看懂了~初入开发:kissing_heart:

      本文标题:位运算和枚举

      本文链接:https://www.haomeiwen.com/subject/dttclttx.html