用枚举变量作标帜位的初学者手册

 用枚举变量作标帜位的初学者手册

    一次我在Visual C++论坛闲逛时(又一次),我不得不面对这样的事实,很多初学者对按位操作,通常是二进制,缺乏常识性了解。在我敲疼了手指给无知的人写一个长长的答案之后,很明显,我不得不通过这篇文章与社区分享这一易混淆的知识。
    如果你想要对C/C++位操作知识有更深入的理解,你可以去读PJArends写的非常全面的文章《位操作介绍》。你也可以进行复杂但有效的位分析(bit hacking),阅读这篇文章:Sean Eron Anderson 写的Bit Twiddling Hacks
    我尽我所能的全部方式来呈现我能够用位操作、标帜的事和其他所有的二进制有关的东西。


事实
    我们发现最常用位操作的情况之一是当一个库提供了一个枚举变量集合并且函数使用DWORD作为标帜位容器。我们以一个enum为例,如下定义:
enum {
    STYLE1 =   1,
    STYLE2 =   2,
    STYLE3 =   4,
    STYLE4 =   8,
    STYLE5 =  16,
    STYLE6 =  32,
    STYLE7 =  64,
    STYLE8 = 128
};
 
 就像我们看到的,这些常量都是2的n次幂,为了理解为什么如此选择这些常量,我们必须研究二进制的表示
   1 -> 0b 00000000 00000000 00000000 00000001
   2 -> 0b 00000000 00000000 00000000 00000010
   4 -> 0b 00000000 00000000 00000000 00000100
   8 -> 0b 00000000 00000000 00000000 00001000
  16 -> 0b 00000000 00000000 00000000 00010000
  32 -> 0b 00000000 00000000 00000000 00100000
  64 -> 0b 00000000 00000000 00000000 01000000
 128 -> 0b 00000000 00000000 00000000 10000000
注意到所有的这些值,每次只有一位被置位,所有的其他的位都等于0。现在你看到了这样的一大好处就是每一位都被作代表一个功能的标帜位(这里每一位代表一种类型)。为了避免使用像标帜位一样多的布尔类型变量,现在我们来想像一种能够混合这些标帜位到一个变量中的方法。考虑下面的例子:
0b 00000000 00000000 00000000 00100101
类型1、3和6被置位

操作

     现在我们面对一个问题。C++不是直接处理二进制的,我们不得不使用按位操作来代替。我们知道有三种原子按位操作符,按优先级升序排列:或(|),和 (&) 非 (~)。它们的行为如下:
    x y  |         x y  &         x  ~
 --------        --------       ------
  0 0  0        0 0  0        0  1
  0 1  1        0 1  0        1  0
  1 0  1        1 0  0
  1 1  1        1 1  1

知道这些,我们现在可以用这些操作来构造上面讲过的混合标帜位。在STYLE1 | STYLE3 | STYLE6这条语句例子中,我们像这样来“或”这些常量。
 0b 00000000 00000000 00000000 00000001     <- STYLE1
 0b 00000000 00000000 00000000 00000100     <- STYLE3
 0b 00000000 00000000 00000000 00100000     <- STYLE6
-----------------------------------------------
 0b 00000000 00000000 00000000 00100101     <- STYLE1 | STYLE3 | STYLE6
 
 我们看到按位操作“或”非常像“加”(+)操作。但如果你希望用+代替|,那么你要非常小心。原因很简单,“加”1+1结果是0(有一个进位)。如果所有的常量都是严格的不同,那么不会有问题 ,但是当处理二进制使用其他的操作而不是按位操作是个坏习惯,我将不深入讨论。

DWORD按位操作
  

     这样的混合标帜一般采用DWORD类型。但是,这不是必须条款,我们也能够用任何整型(char、short、int、long...)来实现。一个DWORD是一个无符号32位整型(就像这篇文章中的二进制表示的那些)。让我们想像这样一种情况,当我们在函数中有一个DWORD,在参数中传递另一个。调用函数怎么知道哪一位被置1了呢?这简单,往下看!
   比如说我们想要知道在参数中被传递的DWORD中是否SYTLE8位被置1。我们必须有一个我们能购与参量求逻辑和的掩码。实践中,掩码与我们测试的常量是一样的,因此不需要额

外的代码来创建掩码:

DWORD parameter   ->   0b 00000000 00000000 00000000 00100101
     STYLE8 mask       ->   0b 00000000 00000000 00000000 10000000
                       ----------------------------------------
        Bitwise AND       ->   0b 00000000 00000000 00000000 00000000     <- 0x00000000
 
 
 
DWORD parameter   ->   0b 00000000 00000000 00000000 10100101
     STYLE8 mask       ->   0b 00000000 00000000 00000000 10000000
                       ----------------------------------------
        Bitwise AND       ->   0b 00000000 00000000 00000000 10000000     <- STYLE8

如果“和”操作返回0,那么这个位将被置1,否则,返回掩码自身。

 好的,现在,实践中下面是你经常可以看到的
void SetStyles(DWORD dwStyles) {
    if ((STYLE1 & dwStyles) == STYLE1) {
        //Apply style 1
    }
    else if ((STYLE2 & dwStyles) == STYLE2) {
        //Apply style 2
    }
    else if ((STYLE3 & dwStyles) == STYLE3) {
        //Apply style 3
    }
    //etc...
}

 我还没有讲第三个位操作“否”(~)。它通常用在你已经有了一个位集,有一些位被置1而另一些没有,并且你不会清除其中的任何一个,下面的样例代码展示了怎样实现这些功能
 void RemoveStyle5 (DWORD& dwStyles) {
    if ((STYLE5 & dwStyles) == STYLE5) {
        dwStyles &= ~STYLE5;
    }
}

我尚未提及另一个位操作“异或”(^), 最后提及它的唯一原因是它不是原子的位操作,也就是说我们不能够同过前面的提到的位操作符来重构它的行为。
#define XOR(a,b) (((a) & ~(b)) | ((b) & ~(a)))

不管怎样,这种操作很容易切换一个位。
void SwitchStyle5 (DWORD& dwStyles) {
    dwStyles ^= STYLE5;
}

链接

下面的链接是一些补充位操作的知识,如果你想要深入了解按位操作的知识
An introduction to bitwise operators   by PJ Arends.
Bit Twiddling Hacks     by Sean Eron Anderson.

结论

 好的,如果你读到了这,我希望我帮助了你,如果你发现这里的错误,那么不要犹豫指出我的错误,我因此而能很快改正。

原文:http://www.codeproject.com/useritems/Binary_Guide.asp

译者:这篇文章我翻译的不是很准确,但基本意思都表达出来了。希望大家指正其中的错误forwhoever@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
适合LTspice初学者的仿真指导手册应该包含以下内容: 1. LTspice软件介绍:简要介绍LTspice软件的功能和特点,包括电路仿真、波形显示、参数分析等功能,帮助初学者对软件有一个整体的了解。 2. 界面和操:详细介绍LTspice的界面布局和操方法,包括工具栏、菜单栏、元件库、仿真设置等,帮助初学者快速上手并进行仿真实验。 3. 元件的选取和使用:介绍常用的电子元件的选择和使用方法,包括电阻、电容、电感、二极管、三极管等,以及它们在电路中的连接和参数设置。 4. 电路建立和仿真:详细介绍电路的建立和仿真方法,包括元件的选择、连接和参数设置,以及仿真的启动和波形显示等步骤,帮助初学者理解仿真的基本原理。 5. 参数分析和优化:介绍LTspice中参数分析和优化的方法和步骤,包括对电路中元件和参数进行变化和优化的仿真实验,帮助初学者掌握参数分析和优化的技巧。 6. 实例分析和实验设计:提供一些常见的电路实例和实验设计,包括放大器电路、滤波器电路、稳压电路等,帮助初学者在仿真中学习和掌握电路设计和分析的方法。 7. 常见问题解答和参考资料:列出一些常见的问题和解答,以及相关的学习资料和参考书目,帮助初学者在学习过程中解决问题和进一步深入学习。 通过以上内容的详细介绍和实践案例的演示,适合LTspice初学者的仿真指导手册能够帮助他们快速入门并掌握LTspice软件的使用方法,同时能够在仿真实验中学习和理解电路的基本原理和设计方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值