找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 3389|回復(fù): 3
打印 上一主題 下一主題
收起左側(cè)

C語言位操作的一些技巧

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:379034 發(fā)表于 2018-7-25 16:29 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
C語言位操作
此文將花費(fèi)您8~15分鐘時(shí)間,帶您對(duì)嵌入式中常用的位操作有個(gè)了解。和數(shù)字電路有點(diǎn)相似!感謝閱讀!位操作符
1.位與&
1、注意:位與符號(hào)是一個(gè)&,兩個(gè)&&是邏輯與。
2、真值表:1&0=01&1=10&0=00&1=0
3、從真值表可以看出:位與操作的特點(diǎn)是,只有1和1位與結(jié)果為1,其余全是0.
4、位與和邏輯與的區(qū)別:位與時(shí)兩個(gè)操作數(shù)是按照二進(jìn)制位彼次對(duì)應(yīng)位相與的,邏輯與是兩個(gè)操作數(shù)作為整體來相與的。(舉例:0xAA&0xF0=0xA0,0xAA && 0xF0=1)
2.位或|
1、注意:位或符號(hào)是一個(gè)|,兩個(gè)||是邏輯或。
2、真值表:1|0=11|1=10|0=00|1=1
3、從真值表可以看出:位或操作的特點(diǎn)是:只有2個(gè)0相位或才能得到0,只要有1個(gè)1結(jié)果就一定是1.
4、位或和邏輯或的區(qū)別:位或時(shí)兩個(gè)操作數(shù)是按照二進(jìn)制位彼次對(duì)應(yīng)位相與的,邏輯或是兩個(gè)操作數(shù)作為整體來相或的。

3.位取反~
1、注意:C語言中位取反是~,C語言中的邏輯取反是!
2、按位取反是將操作數(shù)的二進(jìn)制位逐個(gè)按位取反(1變成0,0變成1);而邏輯取反是真(在C語言中只要不是0的任何數(shù)都是真)變成假(在C語言中只有0表示假)、假變成真。

實(shí)驗(yàn):
#include <stdio.h>
void main()
{
        int a = 5; // 結(jié)果 :
        printf("~~a = %d.\n",~~a);//~~a = 5.
        printf("!!a = %d.\n",!!a);//!!a = 1.
}
結(jié)論:
@任何非0的數(shù)被按位取反再取反就會(huì)得到他自己;任何非0的數(shù)被按邏輯取反再取反就會(huì)得到1;
@任何非0的數(shù)被按邏輯取反再取反就會(huì)得到1;

4.位異或^

1、位異或真值表:
1^1=0
0^0=0
1^0=1
0^1=1
2、位異或的特點(diǎn):2個(gè)數(shù)如果相等結(jié)果為0,不等結(jié)果為1。
記憶方法:異或就是相異就或操作起來。
位與、位或、位異或的特點(diǎn)總結(jié):
位與:(任何數(shù),其實(shí)就是1或者0)與1位與無變化,與0位與變成0
位或:(任何數(shù),其實(shí)就是1或者0)與1位或變成1,與0位或無變化
位異或:(任何數(shù),其實(shí)就是1或者0)與1位異或會(huì)取反,與0位異或無變化

5.左移位<< 與右移位>>
C語言的移位要取決于數(shù)據(jù)類型。
對(duì)于無符號(hào)數(shù),左移時(shí)右側(cè)補(bǔ)0(相當(dāng)于邏輯移位)

對(duì)于無符號(hào)數(shù),右移時(shí)左側(cè)補(bǔ)0(相當(dāng)于邏輯移位)
對(duì)于有符號(hào)數(shù),左移時(shí)右側(cè)補(bǔ)0(叫算術(shù)移位,相當(dāng)于邏輯移位)
對(duì)于有符號(hào)數(shù),右移時(shí)左側(cè)補(bǔ)符號(hào)位(如果正數(shù)就補(bǔ)0,負(fù)數(shù)就補(bǔ)1,叫算術(shù)移位)實(shí)驗(yàn):
#include <stdio.h>
void main()
{
int a = 5; // 0101b
int b = -5;// 1...0101b
printf("a<<2 = %d.\n",a<<2);
printf("a>>2 = %d.\n",a>>2);
printf("b<<2 = %d.\n",b<<2);
printf("b>>2 = %d.\n",b>>2);

}
結(jié)果:
a<<2 = 20.
a>>2 = 1.
b<<2 = -20.
b>>2 = -2.結(jié)論:
左移一位,結(jié)果*2;
右移一位,結(jié)果/2;
注釋:有符號(hào)數(shù)右移結(jié)果往小的那邊走,-5/4=-1.25->-2;
分析過程自己去分析數(shù)值在計(jì)算機(jī)內(nèi)部的存儲(chǔ)(以補(bǔ)碼形式存儲(chǔ)!)
~嵌入式中研究的移位,以及使用的移位都是無符號(hào)數(shù)
位與位或位異或在操作寄存器時(shí)的特殊作用
1.寄存器操作的要求(特定位改變而不影響其他位)
1、ARM是內(nèi)存與IO統(tǒng)一編址的,ARM中有很多內(nèi)部外設(shè),SoC中CPU通過向這些內(nèi)部外設(shè)的寄存器寫入一些特定的值來操控這個(gè)內(nèi)部外設(shè),進(jìn)而操控硬件動(dòng)作。所以可以說:讀寫寄存器就是操控硬件。
2、寄存器的特點(diǎn)是按位進(jìn)行規(guī)劃和使用。但是寄存器的讀寫卻是整體32位一起進(jìn)行的(也就是說你只想修改bit5~bit7是不行的,必須整體32bit全部寫入)
3、寄存器操作要求就是:在設(shè)定特定位時(shí)不能影響其他位。
4、如何做到?答案是:讀-改-寫三部曲。
  讀改寫的操作理念,就是:當(dāng)我想改變一個(gè)寄存器中某些特定位時(shí),我不會(huì)直接去給他寫,我會(huì)先讀出寄存器整體原來的值,然后在這個(gè)基礎(chǔ)上修改我想要修改的特定位,再將修改后的值整體寫入寄存器。
  這樣達(dá)到的效果是:在不影響其他位原來值的情況下,我關(guān)心的位的值已經(jīng)被修改了。

  
2.特定位清零用&
1、回顧上面講的位與操作的特點(diǎn):(任何數(shù),其實(shí)就是1或者0)與1位與無變化,與0位與變成0
2、如果希望將一個(gè)寄存器的某些特定位變成0而不影響其他位,可以構(gòu)造一個(gè)合適的1和0組成的數(shù)和這個(gè)寄存器原來的值進(jìn)行位與操作,就可以將特定位清零。

3、舉例:
假設(shè)原來32位寄存器中的值為:0xAAAAAAAA,我們希望將bit8~bit15清零而其他位不變,可以將這個(gè)數(shù)與0xFFFF00FF進(jìn)行位與即可。
#include <stdio.h>
void main()
{
printf("0x%X.\n",0xAAAAAAAA&0xFFFF00FF);// 0xAAAA00AA.
}

3.特定位置1用|
1、回顧上面講的位或操作的特點(diǎn):任何數(shù),其實(shí)就是1或者0)與1位或變成1,與0位或無變化

2、操作手法和剛才講的位與是類似的。我們要構(gòu)造這樣一個(gè)數(shù):要置1的特定位為1,其他位為0,然后將這個(gè)數(shù)與原來的數(shù)進(jìn)行位或即可。
3、舉例:
假設(shè)原來32位寄存器中的值為:0xAAAAAAAA,我們希望將bit8~bit15置1而其他位不變,可以將這個(gè)數(shù)與0x0000FF00進(jìn)行位或即可。
#include <stdio.h>
void main()
{
printf("0x%X.\n",0xAAAAAAAA|0x0000FF00);// 0xAAAAFFAA.
}

4.特定位取反用^
1、回顧上面講的位異或操作的特點(diǎn):(任何數(shù),其實(shí)就是1或者0)與1位異或會(huì)取反,與0位異或無變化
2、操作手法和剛才講的位與是類似的。我們要構(gòu)造這樣一個(gè)數(shù):要取反的特定位為1,其他位為0,然后將這個(gè)數(shù)與原來的數(shù)進(jìn)行位異或即可。

進(jìn)行位或即可。
3、舉例:
假設(shè)原來32位寄存器中的值為:0xAAAAAAAA,我們希望將bit8~bit15取反而其他位不變,可以將這個(gè)數(shù)與0x0000FF00進(jìn)行異或即可。
#include <stdio.h>
void main()
{
printf("0x%X.\n",0xAAAAAAAA^0x0000FF00);// 0xAAAA55AA.
}
如何用位運(yùn)算構(gòu)建特定二進(jìn)制數(shù)
1.寄存器位操作經(jīng)常需要特定位給特定值
1、從上節(jié)可知,對(duì)寄存器特定位進(jìn)行置1或者清0或者取反,關(guān)鍵性的難點(diǎn)在于要事先構(gòu)建一個(gè)特別的數(shù),這個(gè)數(shù)和原來的值進(jìn)行位與、位或、位異或等操作,即可達(dá)到我們對(duì)寄存器操作的要求。

2、
解法1:用工具軟件或者計(jì)算器或者自己大腦計(jì)算,直接給出完整的32位特定數(shù)。
  優(yōu)勢(shì):可以完成工作,難度也不大,操作起來也不是太麻煩。
  劣勢(shì):依賴工具,而且不直觀,讀程序的人不容易理解。
  評(píng)價(jià):湊活能用,但是不好用,應(yīng)該被更好用的方法替代。
3、解法2:自己寫代碼用位操作符號(hào)(主要是移位和位取反)來構(gòu)建這個(gè)特定的二進(jìn)制數(shù)

2.使用移位獲取特定位為1的二進(jìn)制數(shù)
1、最簡(jiǎn)單的就是用移位來獲取一個(gè)特定位為1的二進(jìn)制數(shù)。譬如我們需要一個(gè)bit3~bit7為1(隱含意思就是其他位全部為0)的二進(jìn)制數(shù),可以這樣:(0x1f<<3)
2、更難一點(diǎn)的要求:獲取bit3~bit7為1,同時(shí)bit23~bit25為1,其余位為0的數(shù):((0x1f<<3)| (7<<23))

3.再結(jié)合位取反獲取特定位為0的二進(jìn)制數(shù)
1、這次我們要獲取bit4~bit10為0,其余位全部為1的數(shù)。怎么做?

2、利用上面講的方法就可以:(0xf<<0)|(0x1fffff<<11)
但是問題是:連續(xù)為1的位數(shù)太多了,這個(gè)數(shù)字本身就很難構(gòu)造,所以這種方法的優(yōu)勢(shì)損失掉了。
3、這種特定位(比較少)為0而其余位(大部分)為1的數(shù),不適合用很多個(gè)連續(xù)1左移的方式來構(gòu)造,適合左移加位取反的方式來構(gòu)造。
4、思路是:先試圖構(gòu)造出這個(gè)數(shù)的位相反數(shù),再取反得到這個(gè)數(shù)。
(譬如本例中要構(gòu)造的數(shù)bit4~bit10為0其余位為1,那我們就先構(gòu)造一個(gè)bit4~bit10為1,其余位為0的數(shù),然后對(duì)這個(gè)數(shù)按位取反即可)- ~(0x7f<<4)

4.總結(jié):位與、位或結(jié)合特定二進(jìn)制數(shù)即可完成寄存器位操作需求
1、如果你要的這個(gè)數(shù)比較少位為1,大部分位為0,則可以通過連續(xù)很多個(gè)1左移n位得到。
2、如果你想要的數(shù)是比較少位為0,大部分位為1,則可以通過先構(gòu)建其位反數(shù),然后再位取反來得到。

3、如果你想要的數(shù)中連續(xù)1(連續(xù)0)的部分不止1個(gè),那么可以通過多段分別構(gòu)造,然后再彼此位或即可。
這時(shí)候因?yàn)閰⑴c位或運(yùn)算的各個(gè)數(shù)為1的位是不重復(fù)的,所以這時(shí)候的位或其實(shí)相當(dāng)于幾個(gè)數(shù)的疊加。

位運(yùn)算實(shí)戰(zhàn)演練1
回顧:要置1用|,用清零用&,要取反用^,~和<< >>用來構(gòu)建特定二進(jìn)制數(shù)。
1.給定一個(gè)整型數(shù)a,設(shè)置a的bit3,保證其他位不變。
a = a | (1<<3)或者 a |= (1<<3)

2.給定一個(gè)整形數(shù)a,設(shè)置a的bit3~bit7,保持其他位不變。
a = a | (0b11111<<3)或者 a |= (0x1f<<3);

3.給定一個(gè)整型數(shù)a,清除a的bit15,保證其他位不變。
a = a & (~(1<<15));或者 a &=(~(1<<15));

4.給定一個(gè)整形數(shù)a,清除a的bit15~bit23,保持其他位不變。
a = a & (~(0x1ff<<15));或者 a &=(~(0x1ff<<15));

5.給定一個(gè)整形數(shù)a,取出a的bit3~bit8。
思路:
第一步:先將這個(gè)數(shù)bit3~bit8不變,其余位全部清零。
第二步,再將其右移3位得到結(jié)果。
第三步,想明白了上面的2步算法,再將其轉(zhuǎn)為C語言實(shí)現(xiàn)即可。
a &= (0x3f<<3);
a >>= 3;

6.用C語言給一個(gè)寄存器的bit7~bit17賦值937(其余位不受影響)。
關(guān)鍵點(diǎn):第一,不能影響其他位;第二,你并不知道原來bit7~bit17中裝的值。

思路:
第一步,先將bit7~bit17全部清零,當(dāng)然不能影響其他位。
第二步,再將937寫入bit7~bit17即可,當(dāng)然不能影響其他位。
a &= ~(0x7ff<<7);
a |= (937<<7);

位運(yùn)算實(shí)戰(zhàn)演練2
1.用C語言將一個(gè)寄存器的bit7~bit17中的值加17(其余位不受影響)。
關(guān)鍵點(diǎn):不知道原來的值是多少

思路:
第一步,先讀出原來bit7~bit17的值
第二步,給這個(gè)值加17
第三步,將bit7~bit17清零
第四步,將第二步算出來的值寫入bit7~bit17

tmp = a&(0x7ff<<7);
tmp>>=7;
tmp+=17;
a &=~(0x7ff<<7);
a|=(tmp<<7);
2.用C語言給一個(gè)寄存器的bit7~bit17賦值937,同時(shí)給bit21~bit25賦值17.
思路:6.的升級(jí)版,兩倍的6.中的代碼即可解決。
分析:這樣做也可以,但是效果不夠高,我們有更優(yōu)的解法就是合兩步為一步。

a &= ~((0x7ff<<7) |(0x1f<<21));
a |= ((937<<7) | (0x17<<21))
技術(shù)升級(jí):用宏定義來完成位運(yùn)算
1.直接用宏來置位、復(fù)位(最右邊為第1位)
#define SET_NTH_BIT(x, n)  (x | (1<<(n-1)))

#define CLEAR_NTH_BIT(x, n) (x &~(1<<(n-1)))

2.截取變量的部分連續(xù)位。例如:變量0x88, 也就是0b10001000,若截取第2~4位,則值為:0b100 = 4
#define GETBITS(x, n, m) ((x &~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1))
分析:這個(gè)題目相當(dāng)于我們位運(yùn)算實(shí)戰(zhàn)演練1中5.做的事情,只不過要用宏來實(shí)現(xiàn)。

這個(gè)題目相當(dāng)于是要把x的bit(n-1)到bit(m-1)取出來
注:優(yōu)先級(jí)~ 高于 <<高于&
#define GETBITS(x, n, m) ((x &(~(~(0U)<<(m-n+1))<<(n-1))) >> (n-1))

U表示unsigned int-32

復(fù)雜宏怎么分析:
((x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1))



第一步,先分清楚這個(gè)復(fù)雜宏分為幾部分:2部分
@(x &~(~(0U)<<(m-n+1))<<(n-1))
@>>(n-1)
分析為什么要>>(n-1),相當(dāng)于是我們5.中的第二步(第二步,再將其右移3位得到結(jié)果。)


第二步,繼續(xù)解析剩下的:又分為2部分

@x&
@~(~(0U)<<(m-n+1))<<(n-1)
分析為什么要&,相當(dāng)于我們5中的第一步 (第一步:先將這個(gè)數(shù)bit3~bit8不變,其余位全部清零。)


第三步,繼續(xù)分析剩下的:
~ (~(0U)<<(m-n+1))<<(n-1)
這個(gè)分析時(shí)要搞清楚第2坨到底應(yīng)該先左邊取反再右邊<<;還是先右邊<<再左邊取反。
解法:第一,查C語言優(yōu)先級(jí)表;第二,自己實(shí)際寫個(gè)代碼測(cè)試。
說明這個(gè)式子應(yīng)該是 ~(~(0U)<<(m-n+1))<<(n-1) ,這就又分為2部分了


0x88:10001000
例如:變量0x88, 也就是0b10001000,若截取第2~4位,則值為:0b100 = 4
~(~(0U)<<(m-n+1))<<(n-1)):00001110
(x & ~(~(0U)<<(m-n+1))<<(n-1)):00001000
(x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1):00001000


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂1 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:380389 發(fā)表于 2018-8-1 19:45 來自手機(jī) | 只看該作者
收藏值得復(fù)習(xí)   學(xué)習(xí)一下技巧
回復(fù)

使用道具 舉報(bào)

板凳
ID:152369 發(fā)表于 2018-8-15 15:31 | 只看該作者
位運(yùn)算說的挺細(xì)
回復(fù)

使用道具 舉報(bào)

地板
ID:370231 發(fā)表于 2018-8-25 10:26 | 只看該作者
學(xué)習(xí)學(xué)習(xí),好好消化一下先
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表