1. 程式人生 > >C語言程式設計開發中的移位操作符

C語言程式設計開發中的移位操作符

1. C語言中的位操作符
 

因為C語言的設計目的是取代組合語言,所以它必須支援組合語言所具有的運算能力,所以C語言支援全部的位操作符(Bitwise Operators)。位操作是對位元組或字中的位(bit)進行測試、置位或移位處理,在對微處理器的程式設計中,特別適合對暫存器、I/O埠進行操作。因而本節將對此作比較詳細地介紹。

6種位操作符的形式與含義如下:
& :按位“與”(AND);
|  :按位“或”(OR);
^ :按位“異或”(XOR);
~ :“取反” (NOT);
>> :資料右移;
<< :資料左移;

1) 按位“與”運算
按位“與”運算子 & 的作用是對運算子兩側以二進位制表達的運算元按位分別進行“與”運算,而這一運算是以數中相同的位(bit)為單位的。操作的規則是:僅當兩個運算元都為1時,輸出的結果才為1,否則為0。
例如:
a = 0x88,b = 0x81,則a & b 的運算結果如下:

    0x88 1000 1000 a數
&    0x81 1000 0001 b數
   =        1000 0000

其中,& 運算子讓a數0x88與B數0x81的1位與1位、2位與2位……7位與7位分別相“與”。由於“與”運算的操作規則是,兩個運算元中各位只要有1個為0,其結果中對應的位就為0。而a數與b數中只有最高位(第7位)均為1,因而該位結果為1,其它各位結果都為0。通常我們可把按位“與”操作 & 作為關閉某位(即將該位置0)的手段,例如我們想要關閉a數中的第3位,而又不影響其它位的現狀,可以用一個數0xF7,即二進位制數1111 0111去與a數作按位“與”運算:
    0x88 1000 1000 a數
 &  0xF7 1111 0111 遮蔽數
   =       1000 0000

注意,這個數除第3位為0外,其它各位均為1,操作的結果只會將a數中的第3位置0,而a數的其它位不受影響。也就是說,若需要某個數的第n位關閉,只需要將該數與另一個數按位相與,另一個數除了相應的第n位為0外,其它各位都為1,以起到對其它各位的遮蔽作用。上面的運算可以用a = a &(0xF7) 來表示,也可以用a & =(0xF7) 來表達。這兩個表示式功能是相同的(見上節“複合賦值運算子”部分),但在源程式程式碼中常常見到的以第二種形式為多。

2) 按位“或”運算
按位“或” 運算子 | 的作用是對運算子兩側以二進位制表達的運算元按位分別進行“或”運算,而這一運算是以數中相同的位(bit)為單位的。操作的規則是:僅當兩個運算元都為0時,輸出的結果才為0,否則為1。
例如:
a = 0x88,b = 0x81,則a | b 的運算結果如下:

  0x88 1000 1000 a數
|  0x81 1000 0001 b數
   =     1000 1001

通常我們可把按位“與”操作 & 作為置位(即將該位置1)的手段,例如我們想要將a數中的第0位和1位置1,而又不影響其它位的現狀,可以用一個數0x03,即二進位制數00000011去與a數作按位“或”運算:

  0x88 1000 1000 a數
| 0x03 0000 0011 遮蔽數
   =    1000 1011

注意,這個數除第0、1位為1外,其它各位均為0,操作的結果只會將a數中的第0、1位置0,而a數的其它位不受影響。也就是說,若需要某個數的第n位置1,只需要將該數與另一個數按位相“或”,另一個數除了相應的第n位為1外,其它各位都為0,以起到對其它各位的遮蔽作用。上面的運算可以用a = a | (0xF7) 來表示,也可以用a | =(0xF7) 來表達。

3) 按位“異或”運算
按位“異或”運算子 ^ 的作用是對運算子兩側以二進位制表達的運算元按位分別進行“異或”運算,而這一運算是以數中相同的位(bit)為單位的。異或運算操作的規則是:僅當兩個運算元不同時,相應的輸出結果才為1,否則為0。
例如:
a = 0x88,b = 0x81,則a ^ b 的運算結果如下:

    0x88 1000 1000 a數
^   0x81 1000 0001 遮蔽數
=            0000 1001

按位“異或”運算 ^ 具有一些特殊的應用,介紹如下:

① 按位“異或”運算可以使特定的位取反
例如:我們想讓a數中的最低位和最高位取反,只要用0x81,即二進位制數10000001去與它作按位“異或”運算,其運算結果同上式。經過操作後,最高位的值已經由1變0,而最低位的值也已經由0變1,起到了使這兩位翻轉的效果。其它位的狀態保持不變。可以看到,這個數除最低位、最高位為1外,其它各位均為0,操作的結果只會將a數中的第0、7位取反,而a數的其它位不受影響。也就是說,若需要某個數的第n位取反,只需要將該數與另一個數按位相“異或”,另一個數除了相應的第n位為1外,其它各位都為0,以起到對其它各位的遮蔽作用。上面的運算可以用a = a ^ (0x81) 來表示,也可以用a ^ =(0x81) 來表達。

② 直接交換兩個變數的值
例如,若有變數a = 3,b = 4,想要交換它們的值,可以做如下一組操作:
a ^ = b
b ^ = a
a ^ = b

首先,a ^ = b:
    a 0000 0011
^ b 0000 0100
a = 0000 0111

其次,b ^ = a:
    b 0000 0100
^ a 0000 0111
b = 0000 0011

最後,a ^ = b:
    a 0000 0111
^ b 0000 0011
a = 0000 0100

這樣,a、b兩個變數中的值就進行了對調。

4)“取反”運算
“取反”運算子 ~ 的作用是將各位數字取反:所有的0置為1,1置為0。例如:1001 0110 取反後為0110 1001。

5) 資料右移
資料右移操作符 > > 將變數的各位按要求向右移動若干位。右移語句的通常形式是:variable >>右移位數
如:
a = 1111 0000;
進行 a = a >> 2 操作後,a = 0011 1100。

6) 資料左移
資料左移操作符 < < 將變數的各位按要求向左移動若干位。左移語句的通常形式是:variable < <左移位數
如:
a = 1111 0000;
進行 a = a << 2 操作後,a =1100 0000。

無論是左移還是右移,當某位從一端移出時,另一端出現的空白將以從外面移入的0(某些計算機是送1,詳細內容請查閱相應C編譯程式使用者手冊)來補充。這說明,移位不同於迴圈,從一端移出的位並不送回到另一端去,移去的位永遠丟失了,同時在另一端只能補上相應位數的0。

移位操作可用於整數的快速乘除運算,左移一位等效於乘2,而右移一位等效於除以2。
如:x = 7, 二進位制表達為:0000 0111,
x < < 1             0000 1110,相當於: x =2*7=14,
x < < 3             0111 0000,相當於: x=14*2*2*2=112
x < < 2             1100 0000,          x= 192
在作第三次左移時,其中一位為1的位移到外面去了,而左邊只能以0補齊,因而便不等於112*2*2=448,而是等於192了。當x按剛才的步驟反向移動回去時,就不能返回到原來的值了,因為左邊丟掉的一個1,再也不能找回來了:
x > > 2              0011 0000,       x=48
x > > 3              0000 0110          x=48/8=6
x > > 1              0000 0011          x=6/2=3

移位操作還可以配合其它位操作夫對暫存器或者資料I/O介面的各個位進行設定、檢測,具體方法見下一節。


2.位操作符的一些實用方法介紹

1) 學會應用複合運算子
如前面所介紹的,位操作運算子可以和賦值運算子“=”一起組成複合運算子。即如下5個:
<<= 、>>=、&=、^=、|=
其中,x << = y,相當於x = x << y;
x >> = y,相當於x = x >> y;
x & = y, 相當於x = x & y;
x ^ = y, 相當於x = x ^ y;
x | = y,   相當於x = x | y;
學會在C語言中使用複合運算子,可以簡化源程式,優化目標程式。

2) C 語言中一些常見的位操作方法
由於我們此處學習C 語言的目的主要是為了開發微控制器的控制程式,為此我們特別關注一下對MPU的暫存器、I/O中某一位的操作語句。假如要對PORTA(埠A)的某些位進行賦值、置0、置1、取反、測試,可能會用到如一下一些語句:

① PORTA = 0x87
給整個PORTA賦值,作用是將1000 0111這個數賦予PORTA,即讓PORTA的第0、1、2和7位置1,其它位清0。

② PORTA = (1<<7)
給整個PORTA賦值,作用等價於PORTA = 0x80,將1000 0000這個數賦予PORTA,將指定的第7位置1,其餘各位置0。只不過這裡包括了兩個步驟,即先是括號中的1<<7操作,表示將0x01這個數左移7位,其值變成0x80,再將它賦予PORTA。

③ PORTA = (1<<7) | (1<< 3) | (1<< 2)
給整個PORTA賦值,作用與②中的操作相同,但是是分別對7、3、2位置1,而將其它各位均置0。它先要分別對三個括號中給定的值進行移位操作,再將它們按位“與”,最後將值賦予PORTA。即:

          1000 0000 (1<< 7)
                0000 1000 (1<< 3)
          |     0000 0100 (1<< 2)
   PORTA =1000 1100

④ PORTA & = 0x80
使PORTA中的指定位清0,等價於PORTA =PORTA & (0x80)。由於0x80的二進位制表達形式為1000 0000,利用其最高位為1,其它各位均為0的特性,作為一個模板將其等於1的那些位(如本例中的第7位)遮蔽起來,使之保持不變,而將其它位清0(不管原來為0還是為1)。因為PORTA與0x80按位“與”的結果如下:
PORTA   = 0x87 1000 0111
             & 0x80 1000 0000
            =           1000 0000
操作後,第7位的原來值1被保留,其它各個位被清0,其中最低的3位原來為1,現在均為0了。

⑤ PORTA & = (1<<7)
它也等價於PORTA & = 0x80:這裡也包括了兩個步驟,即先執行括號中的1<<7操作,將0x01左移7位,其值變成0x80,再將它與PORTA做按位“與”。該操作將除指定的第7位以外的各個位清0。

⑥PORTA & = ~ (1 << 7)
該指令在等號後面加了取反符號 ~ 。與上一條操作的區別是,在與PORTA做按位“與”前,還將0x80先行取反,將1000 0000轉換成0111 1111,再做按位“與”操作。這樣的操作結果是將指定的第7位清零,其它各位保持不變。

⑦ PORTA | = (1<<7)
等價於PORTA = PORTA | (1<<7),這裡也是先執行括號中的1<<7操作,將0x01左移7位,其值變成0x80,再將它與PORTA做按位“或”。若操作前PORTA的初始值為0x07,則:

   PORTA 0000 0111
|  0x80    1000 0000
PORTA =  1000 0111
該操作將最高位置1,其它各位保持不變。
要注意的是,這條指令與PORTA = (1<<7) 相比,雖然都可以使指定的某一位置1,但它們有著不同之處。PORTA =(1<<7) 執行後,雖然某一位被置1了,但其它的位卻被修改了,即不管PORTA的初始值為什麼,原來為1的位都會被0覆蓋,執行的結果總是為1000 0000。而本條指令卻可以將其它位遮蔽起來,在改變要設定的那一位的同時,並不改變其它位的狀態。

3) 巧用C語言中的位操作方法

① 將暫存器的指定位置1或清0
在實際應用中,經常利用:PORTA | = (1<< n) 這條指令將暫存器的任意位置1,而又不影響其它位的現有狀態。比如說,你如果想將第4位置1,就使用:PORTA | = (1<< 4) 就行了。當然,也可以使用:PORTA | = (1<< 7) | (1<< 4 ) | (1<< 0) 這樣的指令一次將設第8、5和1位置1,但又不影響到其它位的狀態。在實際應用中,經常利用:PORTA & = ~ (1<< n) 這條指令將暫存器的任意位清0,而又不影響其它位的現有狀態。比如說,你如果想將第4位清0,就使用:PORTA & = ~ (1<< 4) 就行了。
在啟動nRF905晶片向空中傳送資料時,採用以下函式:

/* ShockBurst 發射資料 */
void nrf905_TxSend(void)
{
   PORTD|=(1<<TRXCE);
   DelayUs(1);//>10us
   PORTD &= ~(1<<TRXCE);
}

其中讓PORTD中控制TRX_CE訊號的那一位先置1,再清0,輸出一高一低的脈衝訊號,就在一個脈衝週期內,完成了一次資料傳送。因為在程式的開頭已經定義TRX_CE訊號為PD6位,即TRXCE = 6,因而上面兩行程式等價於:
PORTD|=(1<< 6);
PORTD &= ~(1<< 6);

② 測試暫存器指定位的狀態
nRF905在接收資料過程中,要分別發出CD、AM和DR訊號,而MPU也要分別對這些位進行檢測,看它們是否變高,若變高,就執行下一步,否則就跳出分支,返回主程式。下面就是對這些位進行檢測的一段函式:
/*檢查接收情況*/
void nrf905_RxRecv(void)
{
while ((PIND&(1<<CD))==0); //CD引腳置1,檢測到載波訊號
while ((PIND&(1<<AM))==0); //一般先AM=1指示地址匹配對
while ((PIND&(1<<DR))==0); //DR=1時表示資料接收對而且Crc正確
//nrf905已經接收到資料
       nrf905_ReadData(0);//讀出nrf905中的資料
}
其中有:while   ((PIND&(1<<DR))= =0); 或者:if   ((PIND&(1<<DR))= =0); 語句,其功能就是對暫存器指定的位進行測試。括號中是一個等式,我們將其拆分開介紹它的作用:
1<<DR:
DR在程式的開始已經被定義為2,(1<<DR)也就是(1<< 2),表示將0x01左移2位,結果為0000 0100;
PIND& (1<<DR):
PIND為PORTD埠的8位引腳的值,PIND& (1<<DR)表示讓它和(1<<DR) 亦即和0000 0100按位相“與”。不管PIND的其它位為何值,由於和0相與,這些位的結果都為0,我們關心的只有第2位的狀態。由於該位與1相與,只要DR為高,就會有:
   PIND     xxx x1xx
&            0000 0100
   結果=   0000 0100

結果的第二位的狀態為1,也就是整個表示式:
(PIND&(1<<DR))= = 0不成立,語句的邏輯值為0。
若DR為低,則有:

PIND     xxxx x0xx
&          0000 0100
   結果= 0000 0000

也就是整個表示式的結果為0,(PIND&(1<<DR))= = 0成立,語句的邏輯值為1。根據括號中邏輯值的情況,while 或者if 語句再決定程式的流向。

相關推薦

C語言程式設計開發移位操作符

1. C語言中的位操作符   因為C語言的設計目的是取代組合語言,所以它必須支援組合語言所具有的運算能力,所以C語言支援全部的位操作符(Bitwise Operators)。位操作是對位元組或字中的位(bit)進行測試、置位或移位處理,在對微處理器的程式設計中,特別適合

C語言程式設計時常見的錯誤,看看你招了嗎?

雲上傘 C編譯的程式對語法檢查並不像其它高階語言那麼嚴格,這就給程式設計人員留下“靈活的餘地”,但還是由於這個靈活給程式的除錯帶來了許多不便,尤其對初學C語言的人來說,經常會出一些連自己都不知道錯在哪裡的錯誤。看著有錯的程式,不知該如何改起,通過對C的學習,積累了一些C程式設計時常犯的錯誤,

Linux C程式設計之一:Linux下c語言開發環境

—恢復內容開始— 今天開始根據Linux C程式設計相關視訊的學習所做的筆記,希望能一直堅持下去。。。 1、開發環境的構成 編輯器:VI; 編譯器:選擇GNU C/C++編譯器gcc; 偵錯程式:應用廣泛的gdb; 函式庫:glibc ; 系統標頭檔案:glibc_header; 2、在安裝L

c++程式設計開發有哪些常見的程式設計開發設計模式?

我們在學習使用c++程式設計開發語言來開發軟體的時候,根據不同的使用場景會用到不同的設計模式。下面我們就一起來了解一下這些模式的特點。 1、C++設計模式之橋接模式(Bridge) 橋接模式屬於先天模式,這裡的先天模式就是說一開始就要把結構搭建好,方便後來的擴充套件,而不是對已經出現的

C語言程式設計,IOS軟體開發,張明生

通過Core Library的文件,我們知道建立顏色有這麼幾個方法: CGColorCreate CGColorCreateCopy CGColorCreateGenericGray CGColorCreateGenericRGB CGColorCreateGeneri

C/C++程式設計學習筆記二:C語言的函式,如何使用指標交換兩個數的值,深入理解指標

 使用外部函式交換兩個變數的值,一個再簡單不過的事情,但是在C/C++中,正確實現該功能反應了你對指標和引用等重要知識的掌握程度。本文列舉了幾種常見的寫法,其中前三種是錯誤的,後兩種是正確的。第四種使

Linux菜鳥的入門-終端環境C語言程式設計

       在Linux的終端環境中進行C語言程式的編寫需要用到Vi編輯器。vi編輯器是Linux系統下標準的編輯器.而且不遜色於其他任何最新的編輯器.在此編輯器中就可以進行C語言程式的編寫。      首先建立一個.C檔案。比如:“vi linnash.c”;    

JNI開發基礎篇:C語言呼叫Java的方法

Life always has many things to bring you down.But what can really bring you down is just yourself. 這一篇來記錄如何在C中實現java方法的呼叫(最基本

微控制器的C語言程式設計所用的語言推薦規範

一、程式風格: 1、嚴格採用階梯層次組織程式程式碼: 各層次縮排的分格採用VC的預設風格,即每層次縮排為4格,括號位於下一行。 要求相匹配的大括號在同一列,對繼行則要求再縮排4格。例如: 2、提示資訊字串的位置 在程式中需要給出的

c語言程式設計進階week3:刪除字串的子串(字串與指標的完美結合)

題目來源自mooc:C語言程式設計進階,僅供個人學習參考使用 #include <stdio.h> #include <string.h> int main(){

C語言程式設計常見的漏洞原因

1. gets gets()函式不檢查緩衝長度,可能導致漏洞。呼叫函式gets(buffer),會把使用者輸入的內容放在buffer中,但是對這個內容沒有檢查長度。如果使用者輸入內容過長,就會覆蓋在buffer之後定義的變數,也就是說使用者可以隨意更改一些程式

C語言結構體添加成員函數

我們 pau 打印 log print class 控制 stdio.h 語言   我們在使用C語言的結構體時,經常都是只定義幾個成員變量,而學過面向對象的人應該知道,我們定義類時,不只是定義了成員變量,還定義了成員方法,而類的結構和結構體非常的相似,所以,為什麽不想想如何

C語言程序編寫犯的錯誤的記錄(一)

C 程序編寫 錯誤 今天學習用到了《C程序設計(第四版)》的求兩個數的最大值的程序devcpp程序:#include <stdio.h>int main(){int max(int x,int y);int a,b,c;scanf("%d,%d",&a,&a

linux系統--C語言程序開發的基本步驟(包含gcc的基本步驟)

什麽 linu 第一步 源文件 inux 文件 c語言 決定 擴展 1、使用vi或者vim編寫程序文件 2、使用gcc把所有的源文件翻譯成計算機認識的格式(編譯) 3、使用./a.out作為命令執行得到的可執行文件 gcc編譯器的工作步驟: 1、處理所有的預處理指令 2、把

C++語言學習(八)——操作符重載

換源 fun 並且 img 51cto cal process 基本 alt C++語言學習(八)——操作符重載 一、操作符重載基礎 1、操作符重載的語法 通過operator關鍵字可以定義特殊的函數,operator本質是通過函數重載操作符。 Type operator

學會了C語言可以開發出很多東西嗎?

dac 是你 ping 動態 方法 快速實現 別人 b2c 高級語言 C語言相比其他高級語言,就像內功和劍法一樣。只會C語言可能並不能寫出一個看起來就很厲害的程序,但C語言是你學好其他一切語言的基礎。有了C語言的底子,再學別的語言就很容易上手了。你能想到的,C語言都能搞,可

C語言計算程序某一個函數或算法的執行時間

lock nis 程序 while tar 計算 stdio.h locks turn 計算程序中某一個函數或算法的執行時間 #include <stdio.h> #include <time.h> #include <stdlib.h>

說說C#語言開發環境的搭建

宇宙 微軟公司 加油 裝包 等我 優秀 新的 frame 也有 C#語言是一門面向對象的編程語言,是微軟公司在吸收了C++語言、Java語言的優點的基礎上,創建的一門更加優秀的編程語言。在實際的學習中,我們總是會不得不面對一個C#語言的集成開發環境的選擇問題。可以使用的集成

詳解!C語言程式設計實現小遊戲“三子棋”

今天我們來程式設計實現一個充滿童趣的小遊戲“三子棋” 先來說一下三子棋的規則: 三子棋又叫九宮棋、圈圈叉叉、一條龍等。 將正方形對角線連起來,或相對兩邊依次擺上三個雙方棋子, 總之只要將自己的三個棋子走成一條線, 對方就算輸了。 不用再過多解釋了,相信大家一定都玩過! 那麼,該

1012 - C語言程式設計教程(第三版)課後習題6.2

1012 - C語言程式設計教程(第三版)課後習題6.2 時間限制:1秒 記憶體限制:128兆 題目描述 輸入一行字元,分別統計出其中英文字母、空格、數字和其他字元的個數。 輸入 一行字元 輸出 統計值 樣例輸入 aklsjflj123 sadf918u324 asdf91u32oa