程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 【CC++語言入門篇】-- 位運算

【CC++語言入門篇】-- 位運算

編輯:C++入門知識

回顧之前的篇幅,C語言的主體部分基本已經介紹完了。之所以沒有介紹C++的相關特性是因為在之前的文章中C和C++在這些方面都有共性,所以在面向對象之前。我們先把這些共性給介紹完。也就是說在介紹面向對象之前,所有的文章都是CC++中都能使用的。從這點上來看,現在正極力奮斗於C++戰線上的初學者還是很有用處的。

本篇繼續沿著這條路線,到本篇為止包括本篇都還不會急於去介紹C++的面向對象的特性。那麼在之前的文章中,可以說基本都把內容給介紹完了。本篇雖然不是大概念,但是在實際的項目中是絕對離不開的。那麼我們就在本篇開始我們的位運算旅程。

首先,位運算到底用來做什麼,用處多不,好像到現在我也沒有怎麼用位運算呢? 很多初學者我相信會有這樣的疑問。那麼本篇就將介紹位運算的強大用途及無限魅力。

位運算跟二進制聯系非常緊密,二進制這個概念相信大家都不陌生,我們的位運算也就是在這些0或1上進行操作。不要說二進制你都不知道。比如:

7的8位二進制為: 0000 0111

7的32位二進制為: 0000 0000 0000 0000 0000 0000 0000 0111

二進制與十進制的換算我就不說了。上面為什麼三個1就表示7,不知道的話就看看書哈。

上面說到了8位和32位,我們知道一個字節(byte)表示8位,那麼二進制的一位就是這個位的意思。int是32位,那麼寫完整數字0的二進制就有32個0。這樣思考起來在後面的位運算上要好理解一點。

先來看看我們經常用到的位運算符:& (按位與)、| (按位或)、^ (按位異或)、~ (按位取反)、>> (按位右移)、<< (按位左移)。


& ( 按位與): 概念上來講就是二進制上按每一位(0或1)進行與運算。 那麼與運算是什麼意思該不用我說吧,就是兩者都是1結果為真。其中一個為0結果為假。這裡不可能有0、1之外的數,這裡是二進制。先看一個8位二進制的例子:

7 & 8  = 0000 0 111 & 0000 1000 = 0000 0000 = 0

7 & 3  = 0000 0111 & 0000 0011 = 0000 0011 = 3

很簡單吧。不用多說了,就是操作0和1。


| ( 按位或): 概念上來講就是二進制上按每一位(0或1)進行或運算。 那麼或運算是什麼意思該不用我說吧,就是兩者都是0結果為假。其它情況都為真。

 

7 | 8  = 0000 0 111  | 0000 1000 = 0000 1111 = 15

7 | 3  = 0000 0111  | 0000 0011 = 0000 0111 = 7


^( 按位異或): 概念上來講就是二進制上按每一位(0或1)進行異或運算。 異或運算簡單講就是相同就為假,不同為真。

7 ^ 3  = 0000 0111  ^ 0000 0011 = 0000 0100 = 4


~( 按位取反): 概念上來講就是二進制上按每一位(0或1)進行取反運算。 取反運算簡單講就是0變1,1變0。

~7  = ~0000 0111 = 1111 1 000 = 0xf8 = 248 (無符號)


>>( 按位右移): 概念上來講就是二進制上按每一位(0或1)進行右移運算。 右移運算簡單講就是將二進制的位整體向右移動。

7 >> 2 = 0000 0111 >> 2  = 0000 0001 = 1 // 這裡向右移動了2位,最低位的兩個1被抹去。

這裡右移兩位等於除了2的2次方,7/4 = 1 在整數除法中則看成是被捨掉了小數部分。


<<( 按位左移): 這個就不說了,與上面右移方向的相反。

 


好了,有了基本的概念。那麼下面就進入實際應用了。


我們都知道顏色,比如你再惹我。我就給你顏色看看。那麼這裡的顏色就是RGB,我們在這裡談24位顏色。也就是RGB中的R(紅)、G(綠)、B(藍)分別占8位。這下有的朋友疑惑了,24位?想想前面的基本數據類型裡,沒有24位的類型啊,怎麼辦呢?

於是,我們便用到了位運算。一個32位的無符號整數,高8位置零。低24位用於表示顏色,到這裡又有朋友想了。低24位怎麼表示?我們都知道顏色通常每個分量是0~255之間,三種顏色存放在24位裡怎麼存?

typedef unsigned char BYTE;

typedef unsigned int    UINT;


BYTE r = 255;

BYTE g = 255;

BYTE b = 255;


我們將三個分量都定成是255,這裡的目的是想表示白色。


UINT color = ( r << 16 ) | ( g << 8 ) | b;

然後這樣就組成了我們的顏色:白色。

那麼這裡的原理很簡單:

0000 0000 1111 1111 1111 1111 1111 1111

這裡的顏色分量我都標識了字體的顏色,看紅色的部分是不是就是左移了16位,其他同理,具體的過程就是:

r << 16

0000 0000 1111 1111 0000 0000 0000 0000

g << 8

0000 0000 0000 0000 1111 1111 0000 0000

b

0000 0000 0000 0000 0000 0000 1111 1111

然後看這3個二進制數按位或運算後就是我們的目標顏色,用十六進制看就是:0x00ffff ff 。0xff就是255。

32位的顏色只是比24位顏色多了一個分量,可以用來做透明。也就是我們上面沒有用到的最高8位。32位也可以將高8位的分量放在低8位,RGB放在高24位。比如:

1111 1111 1111 1111 1111 1111 1111 1111

現在我們知道了color,那麼要取得分一個分量怎麼辦呢?很簡單:

BYTE r = ( color >> 16 ) & 0xff;


BYTE g = ( color >> 8 ) & 0xff;


上面三句相當於逆運算。那麼這裡按位與上一個0xff的原理是什麼呢? 我們看g分量:

color >> 8

0000 0000 0000 0000 1111 1111 1111 1111

0xff

0000 0000 0000 0000  0000 0000  1111 1111

兩者相與,是不是就將紅色分量給去掉了呢?

0000 0000 0000 0000 0000 0000 1111 1111


就只剩下綠色的8個1了。這裡我只是舉的255,因此可能有的朋友會說我直接:

BYTE g = ( color >> 16 ) & 0xff; 這樣也等於255啊。這裡我是舉的一個比較特殊的例子,當這裡r g b不相等的時候,就不能這樣用了,這裡是通用的用法,我們不能特殊化。


再來看16位色的RGB565, 字面上的意思很簡單就是r和g占5位, b占6位。一共是16位。如果是16位我們就不需要一個UINT了,只需要:

typedef unsigned short  UINT16;

 

BYTE r = 255;

BYTE g = 255;

BYTE b = 255;


UINT16 color16 = ( ( r & 0xf8 ) << 8 ) | ( ( g & 0xfc ) << 3 ) | ( ( b & 0xf8 ) >> 3 ); 

 


天啊,有的朋友可能看到這一串就暈了,其實我們碰到這種問題,如果對十六進制數不敏感不熟悉的話你就用WINDOWS自帶的計算器進行算嘛。我們還是一步一步來說明吧。

因為是“565”模式的顏色,那麼r要拋棄掉低3位,只需要高5位。g需要拋棄掉低2位,只要6位,b和r相同,也拋棄低3位。一共加起來就是16位了。那麼要把這16位分別保存這3個分量。同樣是按位或運算。r只剩下高5位,要到UINT16的最高5位,所以需要左移8位。


0000 0000 1111 1000   // 很明顯需要向左移動8位

同樣b分量被拋棄掉低2位後:

1111 1 000 1111 1100  // 很明顯需要向左移動3位

而b分量:

1111 1 111 1 11 1 1111  000     // 很明顯多出兩個0需要向右移動3位

上面的拋棄掉低位的算法不用說了吧,不熟悉的就用計算器算相與後是不是想要的結果。正因為有拋棄,因此16位顏色就沒有24位顏色真實。


問題一: 為什麼要拋棄低位,不拋棄高位?(比如紅色就可以是:r & 0x1f)


上面24位色反過來逆運算獲得每一個分量我們已經知道了,那麼:

問題二: 怎麼獲得RGB565顏色color16中的每一個分量。


上面的顏色了解後,我相信大家對於& | << >>這幾個該沒有什麼問題了吧,當然顏色的組合還有其他的,這裡不是為了介紹顏色。而是為了了解位運算。

位運算很靈活,這裡只是一個基本的介紹。更多的還需要大家多實踐。

了解了上面的幾個運算符,下面介紹剩下的兩個:按位取反和按位異或。

在實際的工作中,通常會有一些狀態需要表示。我們這些狀態又想節約一點空間。於是我們選擇了用一個32位的無符號整數來存放這些狀態。比如:

在游戲裡面,某個玩家的一些狀態也就是我們經常說的BUFF,比如:持續加血,持續加藍,持續加體力,經脈受傷,被點穴等等。於是我們就有一個枚舉:

enum EPLAYER_STATE

{

    EPST_NONE    = 0x00000000,     // 沒有狀態

    EPST_ADDHP  = 0x00000001 ,    // 加血

    EPST_ADDMP  = 0x00000002,     // 加藍

    EPST_ADDSP   = 0x00000004,    // 加體力

    EPST_JMDAM   = 0x00000008,    // 經脈受傷

    EPST_DIANX    = 0x00000010,    // 被點穴

    EPST_XUANY    = 0x00000020,    // 被眩暈

    EPST_ATTCK    = 0x00000040,    // 被攻擊

    // .

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved