C/C++
結(jié)構(gòu)體的一個(gè)高級(jí)特性
――
指定成員的位數(shù)
?
在大多數(shù)情況下,我們一般這樣定義結(jié)構(gòu)體:
struct student
{
????????????? ? unsigned int sex;
????????????? unsigned int age;
};
對(duì)于一般的應(yīng)用,這已經(jīng)能很充分地實(shí)現(xiàn)數(shù)據(jù)了的
“
封裝
”
。
但是,在實(shí)際工程中,往往碰到這樣的情況:那就是要用一個(gè)基本類(lèi)型變量中的不同的位表示不同的含義。譬如一個(gè)
cpu
內(nèi)部的標(biāo)志寄存器,假設(shè)為
16 bit
,而每個(gè)
bit
都可以表達(dá)不同的含義,有的表示結(jié)果是否為
0
,有的表示是否越界等等。這個(gè)時(shí)候我們用什么數(shù)據(jù)結(jié)構(gòu)來(lái)表達(dá)這個(gè)寄存器呢?
答案還是結(jié)構(gòu)體!
為達(dá)到此目的,我們要用到結(jié)構(gòu)體的高級(jí)特性,那就是在基本成員變量的后面添加“
:
數(shù)據(jù)位數(shù)”組成新的結(jié)構(gòu)體:
struct xxx
{
?????????????
成員
1
類(lèi)型成員
1 :
成員
1
位數(shù)
;
?????? ????? ?
成員
2
類(lèi)型成員
2 :
成員
2
位數(shù)
;
?????? ????? ?
成員
3
類(lèi)型成員
3 :
成員
3
位數(shù)
;
};
基本的成員變量就會(huì)被拆分!這個(gè)語(yǔ)法在初級(jí)編程中很少用到,但是在高級(jí)程序設(shè)計(jì)中不斷地被用到!例如:
struct student
{
????????????? ? unsigned int sex : 1;
????????????? unsigned int age : 15;
};
上述結(jié)構(gòu)體中的兩個(gè)成員
sex
和
age
加起來(lái)只占用了一個(gè)
unsigned int
的空間(假設(shè)
unsigned int
為
16
位)。
基本成員變量被拆分后,訪問(wèn)的方法仍然和訪問(wèn)沒(méi)有拆分的情況是一樣的,例如:
struct student sweek;
sweek.sex = MALE;//
這里的
MALE
只能是
0
或
1
,值不能大于
1
sweek.age = 20;
雖然拆分基本成員變量在語(yǔ)法上是得到支持的,但是并不等于我們想怎么分就怎么分,例如下面的拆分顯然是不合理的:
struct student
{
????????????? ??? unsigned int sex : 1;
????????????? ? unsigned int age : 12;
};
這是因?yàn)?/span>
1+12 = 13
,不能再組合成一個(gè)基本成員,不能組合成
char
、
int
或任何類(lèi)型,這顯然是不能
“
自圓其說(shuō)
”
的。
在拆分基本成員變量的情況下,我們要特別注意數(shù)據(jù)的存放順序,這還與
CPU
是
Big endian
還是
Little endian
來(lái)決定。
Little endian
和
Big endian
是
CPU
存放數(shù)據(jù)的兩種不同順序。對(duì)于整型、長(zhǎng)整型等數(shù)據(jù)類(lèi)型,
Big endian
認(rèn)為第一個(gè)字節(jié)是最高位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的高位字節(jié)到低位字節(jié));而
Little endian
則相反,它認(rèn)為第一個(gè)字節(jié)是最低位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的低位字節(jié)到高位字節(jié))。
我們定義
IP
包頭結(jié)構(gòu)體為:
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
?????? __u8?????? ihl:4,
?????? ?????? version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
?????? __u8?????? version:4,
???? ?????? ihl:4;
#else
#error?????? "Please fix <asm/byteorder.h>"
#endif
?????? __u8?????? tos;
?????? __u16?????? tot_len;
?????? __u16?????? id;
?????? __u16?????? frag_off;
?????? __u8?????? ttl;
?????? __u8?????? protocol;
?????? __u16?????? check;
?????? __u32?????? saddr;
?????? __u32?????? daddr;
?????? /*The options start here. */
};
在
Little endian
模式下,
iphdr
中定義:
?????? __u8?????? ihl:4,
?????? ?????? version:4;
其存放方式為:
第
1
字節(jié)低
4
位
?ihl
第
1
字節(jié)高
4
位
?version
(
IP
的版本號(hào))
若在
Big endian
模式下還這樣定義,則存放方式為:
第
1
字節(jié)低
4
位
?version
(
IP
的版本號(hào))
第
1
字節(jié)高
4
位
?ihl
這與實(shí)際的
IP
協(xié)議是不匹配的,所以在
Linux
內(nèi)核源代碼中,
IP
包頭結(jié)構(gòu)體的定義利用了宏:
#if defined(__LITTLE_ENDIAN_BITFIELD)
…
#elif defined (__BIG_ENDIAN_BITFIELD)
…
#endif
來(lái)區(qū)分兩種不同的情況。
由此我們總結(jié)全文的主要觀點(diǎn):
(
1
)
??????
C/C++
語(yǔ)言的結(jié)構(gòu)體支持對(duì)其中的基本成員變量按位拆分;
(
2
)
??????
拆分的位數(shù)應(yīng)該是合乎邏輯的,應(yīng)仍然可以組合為基本成員變量;
要特別注意拆分后的數(shù)據(jù)的存放順序,這一點(diǎn)要結(jié)合具體的
CPU
的結(jié)構(gòu)。
?
?
?
?
該文是由宋寶華處轉(zhuǎn)載而來(lái)的,筆者以前從未知道結(jié)構(gòu)體還可以這樣用法,筆者做過(guò)嘗試,再
VC
下用過(guò)的感受有兩點(diǎn)
1、?????????????
結(jié)構(gòu)體按位拆分時(shí),雖然宋兄提醒不能拆分如文中紅色背景顯示的情況,但是本人試過(guò),并非是不可以的,而且如果
CPU
支持
32
的話,顯然文中的以
16
位來(lái)分配的話也是沒(méi)有達(dá)到要求的。
2、?????????????
按位拆分時(shí)字節(jié)數(shù)目問(wèn)題,我們先看兩例
?????? struct student1
?????? {
????????????? unsigned char sex : 1;
????????????? unsigned int? no : 5;
????????????? char??????? ??age : 7;
????????????? int????????? grade : 10;
?????? };
?
????????????? struct student2
?????? {
????????????? unsigned char sex : 1;
????????????? char??????? ? ?age : 7;
????????????? unsigned int ?no : 5;???????????
????????????? int????????? grade : 10;
?????? };
以上兩例中雖然意思并不大,但是如果按
int
為
2
字節(jié)
16
位
char
為
1
字節(jié)
8
位來(lái)劃分內(nèi)存的話,那么
student1
占用了
6
字節(jié)共
48
位,但是實(shí)際使用了
23
位,另外
25
位沒(méi)定義,而
student2
占用了
3
字節(jié)共
24
位,但是實(shí)際使用也是
23
位。這個(gè)過(guò)程,我把它總結(jié)為前后變量的類(lèi)型不一致時(shí),字節(jié)就重新分配。
3、?????????????
賦值過(guò)程中數(shù)據(jù)編碼問(wèn)題。還看兩例
?????? student1 ss;
?????? ss.age = 255;
?????? student2 st;
?????? st.age= 191;
ss.age
的值為
-1
,而
st.age
的值為
63
,其實(shí)
255
為
11111111
,因?yàn)槭?/span>
7
位,所以采用截?cái)喾绞剑兂?/span>
1111111
,又因?yàn)?/span>
age
是有符號(hào)的變量,所以根據(jù)負(fù)數(shù)的編碼規(guī)則賦值
255
時(shí)得到的結(jié)果就是
-1
。在這里采用了截?cái)嗟姆绞剑瑸橹拐_賦值時(shí)一定不能大于位數(shù)編碼值。
以上是個(gè)人學(xué)習(xí)宋兄文章的小小實(shí)踐小結(jié),歡迎大家給出更理論的總結(jié)。原文請(qǐng)查看
http://blog.donews.com/21cnbao/archive/2006/10/07/1054807.aspx
posted on 2006-10-20 00:05
frank.sunny 閱讀(6666)
評(píng)論(7) 編輯 收藏 引用 所屬分類(lèi):
C/C++學(xué)習(xí)和實(shí)踐