|
楼主 |
发表于 2014-7-29 16:34
|
显示全部楼层
Win32 API讲座 第六课∶绘图函数
第六课∶绘图函数
一、位操作
前几天,在很远很远又是那么远的地方,有位网友来信问一些有关位操作的内容。我一开始不大注意这个环节,认为估计大家都能知道。可现在来仔细一想,也并非如此。《Win32 API开发人员指南》一书中也讲了一些位操作的内容,但它位于一开始的象是概论的部分。那么,我想,对位操作不太熟悉的朋友,可以通过以下我对这位网友的回答,学习或加深一些认识。
这位网友的提问非常认真,叫我不得不也跟着认真回答。以下是来信中的一段内容∶
"Not" "And" "Or" 的用法?(因为资料里只是讲了什么"True"呀"False"呀,请麻烦您了)经过实践(正所谓实践出真知嘛),得到以下结论:
12 And 15 =12 '简化了的---返回小
12 Or 15 =15 '返回大
12 And 16 =0 '返回零??
12 Or 16 = 28 '返回 相加??
Not 12 = -13 '(取整数部分+1)的相反数
Not 100 = -101 '(取整数部分+1)的相反数
以下是我的回复内容∶(对原文做了很多改动和加工。)
是位操作运算符。
例如 3 and 1 = 1 来说,为什么结果会等于1呢?
原因是这样的。3等于是11(2进制),1等于是01(2进制) (这里只考虑了两位,实际一般是8位、16位和32位等)
And就是斖?睌的意识。二进制数据中,1代表真(True),0代表假(False)。其运算规则就是∶在两个数相对应的位中,如果两个位同时是1则得1,否则就得0。
具体来讲,11 和 01,首先底位都同时是1,因此得到1,而高位来说前一个数(11)是1,而另一个(01)则是0,这样就不符合斖?睌的要求了,结果得到0。这样11 and 01 就等于是01了 。二进制的01就是十进制的1。因此 可以判断 3 And 1=1 啦。不知,学会了没有?
不访拿你的例子来说,比如 ∶12 And 15=12。结果为什么会等于12呢?
让我们分析一下。首先将各数据转换为2进制数,如下∶
12=1100
15=1111
这样从左开始按斖?笔?则得1,否则得0 數脑?蚓涂梢缘玫?100。表示如下∶
1110
1111 and
1100
我们知道二进制的1100是等于十进制的12,因此可以得出12 And 15 =12的结论
接下来看一看or运算吧。意识当然是《或者》啦。两个数相对应的位中,只要有一个是1就能得到1,所谓∶这个等于1 或者那个等于1,反正有一个是1就得1,否则得0。
再看你的一个例子∶
12 Or 15 =15
按照前述类似的步骤可得∶
12=1100
15=1111
然后按照刚才讲的规则,可做如下的计算∶
1100
1111 or
1111
我们知道二进制的1111是等于十进制的15,因此可以得出12 or 15 =15的结论
or 操作一般用于位设定。比如说 (现在我是举例,并不一定这样) ,某一个数值代表一个窗体的样式,我们一般称之为样式位数据。第一位=1 则表示窗体有标题栏,第2位等于1则表明窗体有滚动条。其他各位也表示其他的信息,但现在我们只讨论这两个位。现在,我们要使窗体不但具有标题栏,而且还想让它具有滚动条,
一般采取的办法是,先用某特定函数获得这个窗体的样式位数据(在WIN32中,实际的窗体样式位数据经常是Long类型的数据),比如我们得到了一个样式位数据MYWINDOWSTYLE=12,当然等于是1100(二进制)。看它的第一位和第二位都是零,说明当前窗体确实没有标题栏和滚动条。要让窗体具有标题栏和滚动条,我们需要做到使其第一位和第二位都变成1。
为此需要两个常数。当然这个常数自然是由API文本游览器提供的(这里是作为例,文本游览器中根本没有与此一致的内容)
Const WS_CAPTION=1
Const WS_VSCROLL=2
(注∶1=0001,2=0010)
现在我们可以通过or操作进行具体的位设定了。如下∶
MYWINDOWSTYLE = MYWINDOWSTYLE or WS_CAPTION or WS_CAPTION
其结果将等于MYWINDOWSTYLE =15,即1111。(1100 or 0010 or 0001 =1111)
最后,我们把这个数据,通过某一函数再传送到实际的窗体处理函数当中,窗体的样式就变化了,变成一个具有标题栏和滚动条的窗体。
这样,or 就变得有点象"同时"的意识了,如∶具有标题栏的 同时 又具有滚动条。但实际运算中仍然是表现为 或者 。千万不要混淆啦。
Not 运算符就有点怪。如果凭空猜测的话很可能猜测成1则得0,0则得1。那么究竟是不是这样呢?其实是这样。但以下的事实会使一些朋友惊奇∶
即Not 12= -13
因为,按上述的规则得出结论的话Not 12,即Not 1100似乎应当等于是0011, 即3,可结果竟是 -13。那么该如何解释这一事实呢?
其实,通常我们在数字的前面冠以?敾驍-敺?爬幢硎臼?恼?海??诩扑慊?惺怯脭0
敽蛿1?0表示正,1表示负)来表示的。如果按8位来考虑问题的话,12应当是00001100。那么通过Not运算以后,它应当是11110011了。
那么11110011又应该如何解释呢?请看,左边第一个1算是说明符号敚瓟吧,不过剩余的7位,即1110011也并不等于13呀?应当是等于115。 -115 ? 这是怎么一回事?
原来呢,这里牵涉到补码计算问题。补码的计算规则是这样的。
如果第一位(高段)为0,表示正数,用其他各位来表示数值部分;但如果第一位为1,表示负数,数值部分可用以下算法来获得∶
设数据X的数据位总数为n,各位依次表示为 x1,x2...xn,那么
X补=2^(n-1)-0x2...xn
(注∶这是整数补码定义,小数补码定义有所不同,读者可以自己翻阅有关材料)
也就是说11110011=-(2^7-01110011)=-(10000000-01110011)=-(00001101)=-13
这么麻烦?不用担心,有个很好的演算规则,这就是∶在原有的数据上加1后把符号取反。所以这也就很好理解了。
VB中TURE=-1的。Not(-1) = 0 , Not 0 = -1 ,您已经能够理解这一点。也因此Not TURE=FLASE,Not FLASE=TURE。但在IF判断语句中,您应当小心使用NOT运算符。
If 语句会把 所有不等于0的数据看成是TURE(条件成立)。有必要忠告您,如果您不能断定某一个数据n (不是逻辑型数据)肯定是-1或0两个数之间的一个数据(但我仍然不推荐这种余地),千万别用Not运算符号(这是我推荐的)。比如n=1的时候,If 1 then也好,
If Not -1 then也好,都是一个样,都是满足If语句的条件成立。很多API函数是返回1的。而且API函数中不大能看到逻辑型数据,反正到目前为止我还没有看到。所以If Not n Then这样一个语句应当写成If n<>0 then 或者If n=0 then的形式来表示。尤其是在API,请记住在API千万要这样。
现在回过头来想一想And和Or运算。请问,在那里不存在补码运算吗?也是存在的。只是我们还没有讨论有负数的情况。您可以自己研究看看,很容易就能明白到的。
好了继续来,让我们继续讨论我们的Not。当然以下的算式对您来说已经不是什么加一个 ? 号的事情了。
Not 12= -1×(12+1)= -13
Not 13= -1×(-13+1)= 12
现在,请您不要想别的事情,不要老想那位白天在大街上看到的长得不怎么样的小姑娘,来记住这样一个要点∶Not运算符同And运算符相结合常用于 清除位 位操作运算。
比如∶
x%=x% and (not &h0001%) 作用是设置数据x%(x% 指的是Integer数据类型的数据x)的第1位为0。
再给出一个例子。以下运算用于清除位9
x%=x% and (not &h0200%)
为什么呢。因为,&h0200%=0000000100000000 ,呵呵,不用我再多一嘴吧?
那么为什么会有这种结果呢。对此我不想在这里深入了。因为,在上面,我都已经讲了,只要你按照这些规则认真思考一下,或者算一算就可以弄清楚其中的奥秘了。
位操作中还有其他一些运算符,比如xor等,感兴趣的朋友,就自己那个什么好了。另外,今后您可能遇到敼庹ぴ怂銛这样的名词,不是别的,就是位运算。
好了。接下来,一边回顾上述内容,一边来看看实际的应用程序吧。以下给出的程序代码是教程三中的一个附带应用程序的代码内容。这个程序的功能是,使单选框控件的那个小圆空来回设置到靠左和靠右。大概,您已经想起来了吧。
Option Explicit
Private Declare Function GetWindowLong& Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long)
Private Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long)
Private Const BS_LEFTTEXT& = &H20&
Private Sub Command1_Click(Index As Integer)
Dim f&, dl&
f& = GetWindowLong(Option1.hWnd, GWL_STYLE)
Debug.Print f& '这里设一个
If Index = 0 Then
f& = f& Or BS_LEFTTEXT
Else
f& = f& And Not BS_LEFTTEXT
End If
Debug.Print f& '这里,再设一个
dl& = SetWindowLong(Option1.hWnd, GWL_STYLE, f&)
Option1.Refresh
End Sub
其实很简单,f& = GetWindowLong(Option1.hWnd, GWL_STYLE) 用来获取样式位数据 f& 。
然后,结合上面讲的内容,首先是∶
f& = f& Or BS_LEFTTEXT 的作用是设置数据f&的第6位为1。
这是因为&h20&=0000000000000000000000000100000 (注∶后面的& 说名此数据是长整型数据,共32位)
其次就是上面所说的《Not运算符同And运算符相结合 常用于 清除位》这句话的具体应用了,即把f& 的第6位恢复为0。
f& = f& And Not BS_LEFTTEXT
可以看到Not运算符的优先级比And大(先计算Not)。最后,把改动的样式位数据回放到控制中∶
dl& = SetWindowLong(Option1.hWnd, GWL_STYLE, f&)
为了便于观察,在上述的代码中,我特别加了两行 Debug.Print f& 调式代码 。我想,您应当知道他们会起什么作用
程序运行结果如下(两个Command1各被点击了一下,Index =0和Index=1)∶
1409359876
1409359908
1409359908
1409359876
可以看出,第一次点击过程中(Index=0),数据从1409359876 变成 1409359908 ,然后在第二次点击过程中(Index=1),从 1409359908恢复到了 1409359876
现在可以比较一下这两个数据(可以用WINDOWS提供的科学型计算器来转换看看∶10进制到2进制),如下∶
1409359876 = 1010100000000010010000000000100
1409359908 = 1010100000000010010000000100100
是不是所谓水落石出 ?
|
|