在求職筆試中,C中的位域是一個(gè)常考點(diǎn),特別是在嵌入式軟件中更常見(jiàn)。位域的最大好處是可以根據(jù)自己需要定制位數(shù),從而節(jié)省空間,例如:嵌入式編程稀缺的內(nèi)存資源。還有在網(wǎng)絡(luò)通訊中,對(duì)頭信息部分的結(jié)構(gòu)定義也常用到位域,少傳一位是一位啊。
這里來(lái)分析EMC的一道筆試題(07年招聘試題):
1 typedef struct bitstruct
2 {
3 int b1:5;
4 int :2;
5 int b2:2;
6 }bitstruct;
7 int main(int argc, char *argv[])
8 {
9 bitstruct b;
10 printf("%d\n",sizeof(bitstruct));
11 memcpy(&b,"EMC Examination",sizeof(b));
12 printf("%d,%d\n",b.b1,b.b2);
13 return 0;
14 }
請(qǐng)問(wèn)在little-endian systems(系統(tǒng)默認(rèn)的存放順序)中,輸出結(jié)果是什么?
答案是
4
5,-2;
所需知識(shí)點(diǎn):
1.位域的概念和特點(diǎn) C語(yǔ)言允許在一個(gè)結(jié)構(gòu)體中以位為單位來(lái)指定其成員所占內(nèi)存長(zhǎng)度,這種以位為單位的成員稱為“位段”或稱“位域”(bit field)。(1)位段成員的類型必須指定為unsigned或int類型;(2)若某一位段要從另一個(gè)字開(kāi)始存放,用:0長(zhǎng)度為0的空位段,作用就是使下一個(gè)位段從下一個(gè)存儲(chǔ)單位(視不同編譯系統(tǒng)而異)開(kāi)始存放;(3)一個(gè)位段必須存儲(chǔ)在同一存儲(chǔ)單元中,不能跨兩個(gè)單元;(4)可以定義無(wú)名字段例如":2";(5)位段的長(zhǎng)度不能大于存儲(chǔ)單元的長(zhǎng)度,也不能定義位段數(shù)組;(6)位段可以用整形格式符輸出;(7)位段可以在數(shù)值表達(dá)式中引用,它會(huì)被系統(tǒng)自動(dòng)地轉(zhuǎn)換成整形數(shù)。[1][3]
2.Little-endian systems的內(nèi)存布局特點(diǎn) 先問(wèn)一個(gè)問(wèn)題:Endian這個(gè)詞是什么意思?
“endian”這個(gè)詞出自《格列佛游記》。小人國(guó)的內(nèi)戰(zhàn)就源于吃雞蛋時(shí)是究竟從大頭(Big-Endian)敲開(kāi)還是從小頭(Little-Endian)敲開(kāi),由此曾發(fā)生過(guò)六次叛亂,其中一個(gè)皇帝送了命,另一個(gè)丟了王位。
我們一般將endian翻譯成“
字節(jié)序”,將big endian和little endian稱作“大尾”和“小尾”。這也在當(dāng)今的CPU派別中一樣存在。Motorola的PowerPC系列CPU采用的Big-endian, Intel的X86系列CPU采用的是Little-endian。Little-endian的特點(diǎn)是高高低低,即高位地址存放最高有效字節(jié),低位地址存放最低有效字節(jié);而B(niǎo)ig-endian正好相反。下面用圖的方式說(shuō)明起來(lái)更直觀,例如0x12345678(
特別注意,這是單個(gè)數(shù),不是字符串,如果是字符串就不一定這樣了。之前沒(méi)有特別注意這點(diǎn),害的我多花了冤枉時(shí)間)。
Big Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
上面的字節(jié)序的不同,在單機(jī)的操作中沒(méi)有問(wèn)題,因?yàn)橐慌_(tái)單機(jī)就是采用單一的字節(jié)序嘛!但是在兩個(gè)不同的主機(jī)進(jìn)行協(xié)作時(shí),就會(huì)出現(xiàn)問(wèn)題了;另外在網(wǎng)絡(luò)通訊中也同樣會(huì)出現(xiàn)問(wèn)題,詳細(xì)參考[2]
就單個(gè)字節(jié)而言,也會(huì)有這樣的問(wèn)題:比特序有差別嗎?
也分成兩種序,如果我們處理的基本單位是字節(jié)以上的話,對(duì)此就不必?fù)?dān)心了,因?yàn)镃PU存儲(chǔ)操作的最小單元是字節(jié),所以比特位的順序?qū)ξ覀儊?lái)就是透明的,我們?cè)谧x取某個(gè)字節(jié)時(shí),不管它用的是Big endian 還是Little endian,我們讀到的都是一個(gè)同樣的字節(jié),只不過(guò)硬件在讀寫時(shí)的順序,一個(gè)是從高到底另一個(gè)是從低到高,對(duì)我們的使用不產(chǎn)生影響。但是如果涉及到位域的存放問(wèn)題,還是要特別小心,上面這道題就是一個(gè)非常的好的例子。
Big Endian
msb lsb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
lsb msb
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3.memcpy()與strcpy()的區(qū)別 這個(gè)問(wèn)題很簡(jiǎn)單,可以想象成這樣:memcpy的基本單位是位,strcpy的基本單位是字符。所以,memcpy會(huì)根據(jù)提供的長(zhǎng)度完全按位拷貝,而strcpy是兩個(gè)字符串之間的拷貝。
回來(lái)原來(lái)的問(wèn)題上,在采用little endian的BUS64中,struct bitstruct在沒(méi)涉及到高低位問(wèn)題時(shí),也就是我們平時(shí)常會(huì)畫出的一種形式是:
{b1 b1 b1 b1 b1 Ø Ø b2 b2 Ø Ø Ø Ø Ø Ø Ø ØØØØØØØØ ØØØØØØØØ};
在內(nèi)存中的實(shí)際布局是:
低地址 高地址
-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| b2 Ø Ø b1 b1 b1 b1 b1 |Ø Ø Ø Ø Ø Ø Ø b2 |ØØØØØØØØ |ØØØØØØØØ |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Ø表示空填充,這當(dāng)中包含一個(gè)原則:一個(gè)位域的存放不可以跨字存放,但可以跨字節(jié)存儲(chǔ);這里采用的比特續(xù)也是little endian,說(shuō)明了比特序?qū)Σ僮饕彩怯杏绊懙穆铮?br>
memcpy按位拷貝“EMC Examination”到b中,一個(gè)字符是8位; 又因?yàn)閟izeof(b)=4,所以只寫入"EMC "。"EMC "對(duì)應(yīng)的位序列是:{0100 0101 0100 1101 0100 0011 0010 0000},從這里寫到內(nèi)存里的形式是:(在這里不要被little endian給迷惑了,E不是放在高地址,參看上面
紅色的特別注意)
低地址 高地址
-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| b2 Ø Ø b1 b1 b1 b1 b1 |Ø Ø Ø Ø Ø Ø Ø
b2 |ØØØØØØØØ
|ØØØØØØØØ
|
| 0 1 0 0 0 1 0 1
|0 1 0 0 1 1 0 1
|0100 0011
|0010 0000
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
printf("%d,%d\n",b.b1,b.b2);讀出來(lái)的b1是00101,它的值是5; b2是10,轉(zhuǎn)換成十進(jìn)制是-2。
到了這一步,似乎題目已經(jīng)解答出來(lái)了,但還有兩個(gè)地方有疑惑:
1.struct bitstruct在內(nèi)存中的位存放順序是這樣的么?
低地址 高地址
-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | b2 Ø Ø b1 b1 b1 b1 b1 |Ø Ø Ø Ø Ø Ø Ø
b2 |ØØØØØØØØ
|ØØØØØØØØ
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.memcpy函數(shù)在按位拷貝時(shí)的拷貝順序是這樣的么?
低地址 高地址
-------------------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 01000101
| 01001101
|0100 0011
|0010 0000
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+關(guān)于上面兩個(gè)問(wèn)題的驗(yàn)證工作留給路過(guò)的朋友來(lái)做:-P ..... 歡迎批評(píng)指正
[1]譚浩強(qiáng) C程序設(shè)計(jì) 第二版 清華大學(xué)出版社
[2]http://blog.csdn.net/sunshine1314/archive/2008/04/20/2309655.aspx
[3]http://topic.csdn.net/t/20061207/10/5212742.html#
[4]http://elanso.com/ArticleModule/LmT3M6M6HGKzTDQcPKMGKAIi.html
[5]關(guān)于字節(jié)序與編碼的關(guān)聯(lián)請(qǐng)參考http://elanso.com/ArticleModule/LmT3M6M6HGKzTDQcPKMGKAIi.html