位标志
由于有读者对《C#程序设计基础教程与实训》214页的那个例子无法理解,所以专门写一篇文章对位标志进行讲解,希望可以对初学者有一些帮助。要理解这篇文章,首先要知道什么是位运算,什么是二进制,这些是计算机一级的内容,我不打算在这里讲解。如果有不懂的请上网用Google了解或查找相关书籍了解,写这篇文章的目的在于让你知道为什么要使用位运算,使用它会带来什么好处。
我们首先从一个问题着手:如果你正在编写一个课程管理系统,你会以什么样的形式来表示你今天哪些节有课哪些节没课呢?比如一天有12节课,如下图所示
可能大家会问,为什么只是记录哪些节有课,哪些节没课呢?这样的信息有什么作用呢?当然有用,比如你要查找某同学今天上午1、2节是否有课,又比如在排课系统中,把某门课排到星期二下午6、7节,首先就要确定这两节课是否已经分配给了其他课程。。。。。。
我想大部分人最初能想到的就是使用一个bool数组来表示它(这大部分人当中当然也包括我自己),使用代码来表示就是下面这个样子:
bool[] arrCourse = new bool[13];
arrCourse[5]=true;
arrCourse[6]=true;
好!恭喜你,有了一个很不错的开始,很好地完成了任务,以上代码执行完的结果如下图所示:
在数组中,当元素值为T时,表明这一节有课,为F时,表明这一节没课。现在感觉相当良好,现在可以开始排课了。比如我们现在需要在某天排入某门课,这一天现在的课程状态如下图所示:
如果我们要把这门课排到第1、2节,就需要做出如下判断
if(arrCourse[0]==false && arrCourse[1]==false)
如果我们需要把排到1、2、3节,就需要做出如下判断
if(arrCourse[0]==false && arrCourse[1]==false && arrCourse[2]==false)
如果需要把课排到1~5节或1~13节。。。!现在有点晕了,怎么这么麻烦啊!当然,你要是真的这么写程序那就是真的有点麻烦了,这样的判断应该借助循环。但无论怎么样,判断的次数是不能减少的,这样的数据结构也不能让人满意。是否有更好的解决方案呢?
好!我们尝试把F变为0,T变为1,看看效果怎么样:
这幅图表示的不是一个二进制数吗?用计算器来转换一下,它就是十进制的902。哈哈!真是天才的想法,用一个整数就可以表示这一天的上课状态了。现在我要把新课排在1、2节只需要把902跟二进制的(110000000000)也就是十进制的3072进行“与”运算(902 & 3072)就OK了,结果为0,表明可以排进去,结果不为0表明1、2节至少有一节已排入其他课了。
如果要排入的是下午6、7节,只需跟二进制(000001100000)也就是十进制的96进行“与”运算。现在不再需要进行多次判断,只需一次运算,判断结果是否为0就解决了所有问题。
现在大家应该明白位运算有多好了吧!Windows中存在着大量的位标志,如果曾经使用Windows API进行编程应该对此有很深的体会。举个例子:假设Windows字体存在以下几种样式:加粗,斜体,下划线。一个字体即可以同时拥有以上三种样式,也可以只拥有一种样式,或拥有其中任意两种样式,或者干脆一种都没有。如何表示字体的样式呢?当然最佳的方案就是使用位标志。如用第一个位表示字体是否加精,第二、三个位分别表示是否是斜体和下划线,如下图所示:
当位标志为:010------(十进制的2)时,表示这个字体只有斜体这种样式
当位标志为:011------(十进制的3)时,表示这个字体为粗体,同时又是斜体
。。。。。。。
所以当你要把某行字变为粗体时,只需让它跟二进制的(001)进行“或”运算就行了
把某行字变为斜体时,只需让它跟二进制的(010)进行“或”运算
把某行字变为带下划线时,只需让它跟二进制的(100)进行“或”运算
如果要把粗体去掉呢?只需把001求补变为110,然后跟位标志进行“与”运算就行了
把某行字的斜体去掉,只需把010求补变为101,然后跟位标志进行“与”运算就行了
把某行字的下划线去掉,只需把100求补变为011,然后跟位标志进行“与”运算就行了
好,现在你应该理解了课本216页的那些古怪代码了吧!当然,字体样式不止这些,加粗是否放在第一个位我也不知道,但道理就是这样的。如果还不明白,请先把位运算学好了再来看这篇文章。课本的第四章也对几个位运算符&,|,~做了详细的讲解。