C語言中用結構實現位段--個人心血!值得一看哦!C語言中的結構是有實現位段的能力的,噢!你問它到底是什麼形式是吧?這個問題呆會給你答案。讓我們先看看位段的作用:位段是在欄位的聲明後面加一個冒號以及一個表示欄位位長的整數來實現的。這種用法又被就叫作“深入邏輯元件的編程”,如果你對系統編程感興趣,那麼這篇文章你就不應該錯過!
我把使用位段的幾個理由告訴大家:1、它能把長度為奇數的資料封裝在一起,從而節省儲存的空間;2、它可以很方便地訪問一個整型值的部分內容。
首先我要提醒大家注意幾點:1、位段成員只有三種類型:int ,unsigned int 和signed int這三種(當然了,int型位段是不是可以取負數不是我說了算的,因為這是和你的編譯器來決定的。位段,位段,它是用來表示欄位位長(bit)的,它只有整型值,不會有7.2這種float類型的,如果你說有,那你就等於承認了有7.2個人這個概念,當然也沒有char這個類型的);2、成員名後面的一個冒號和一個整數,這個整數指定該位段的位長(bit);3、許多編譯器把位段成員的字長限制在一個int的長度範圍之內;4、位段成員在記憶體的實現是從左至右還是從右至左是由編譯器來決定的,但二者皆對。
下面我們就來看看,它到底是什麼東西(我先假定大家的機器字長為32位):
Struct WORD
{
unsigned int chara: 6:
unsigned int font : 7;
unsigned int maxsize : 19;
};
Struct WORD chone;
這一段是從我編寫的一個文字格式化軟體摘下來的,它最多可以容納64(既我說的unsigned int chara :6; 它總共是6位)個不同的字元值,可以處理128(既unsigned int font : 7 ;既2的7次方)種不同的字型,和2的19次方的單位長度的字。大家都可以看到maxsize是19位,它是無法被一個short int 類型的值所容納的,我們又可以看到其餘的成員的長度比char還小,這就讓我們想起讓他們共用32位機器字長,這就避免用一個32位的整數來表示maxsize的位段。怎麼樣?還要注意的是剛才的那一段代碼在16位字長的機器上是無法實現的,為什嗎?提醒你一下,看看上面提醒的第3點,你會明白的!
你是不是發現這個東西沒有用啊?如果你點頭了,那你就錯了!這麼偉大的創造怎麼會沒有用呢(你對系統編程不感興趣,相信你會改變這麼一個觀點的)?磁碟控制卡大家應該知道吧?軟碟機與它的通訊我們來看看是怎麼實現的下面是一個磁碟控制卡的寄存器:
│←5→│←5→│←9→│←8→│←1→│←1→∣←1→∣←1→∣←1→∣
上面位段從左至右依次代表的含義為:5位的命令,5位的扇區,9位的磁軌,8位的錯誤碼,1位的HEAD LOADED,1位的防寫保護,1位的DISK SPINNING,1位的錯誤判斷符,還有1位的READY位。它要怎麼來實現呢?你先自己寫寫看:
struct DISK_FORMAT
{
unsigned int command : 5;
unsigned sector : 5;
unsigned track : 9 ;
unsigned err_code : 8;
unsigned ishead_loaded : 1;
unsigned iswrit_protect : 1;
unsigned isdisk_spinning : 1;
unsigned iserr_ocur : 1;
undigned isready :1 ;
};
註:代碼中除了第一行使用了unsigned int 來聲明位段後就省去了int ,這是可行的,詳見ANCI C標準。
如果我們要對044c18bfH的地址進行訪問的話,那就這樣:
#define DISK ((struct DISK_FORMAT *)0x044c18bf)
DISK->sector=fst_sector;
DISK->track=fst_track;
DISK->command=WRITE;
當然那些都是要宏定義的哦!
我們用位段來實現這一目的是很方便的,其實這也可以用移位或屏蔽來實現,你嘗試過就知道哪個更方便了!
我們今天的話題就到這兒,如果諸位還有疑問,可e-mail給我:arhuwen@163.com;
特別聲明哦:不要把以上內容用於不法行為,否則後果自負。另外本文不可用於任何謀取商業利益的舉動,否則同上!
C編譯器對結構空間預設的分配
在C語言中,結構是一種複合資料型別,其構成元素既可以是基礎資料型別 (Elementary Data Type)(如int、long、float等)的變數,也可以是一些複合資料型別(如數組、結構、聯合等等)的資料單元。在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間;各個成員按照它們被聲明的順序在記憶體中順序儲存,第一個成員的地址和整個結構的地址相同。在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間,見表1:
表1:Win32下的自然對界條件
例如,下面的結構各成員空間分配情況1:
struct test {
char x1;
short x2;
float x3;
char x4;
};
圖1:預設結構空間分配
結構的第一個成員x1,其位移地址為0,佔據了第1個位元組。第二個成員x2為short類型,其起始地址必須2位元組對界,因此,編譯器在x2和x1之間填充了一個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。
結構中的位段
所謂位段是以位為單位定義長度的結構體類型中的成員。編譯器對結構中位段的分配遵從下面幾點原則:
? 對於長度為0的位段,其下一個位段從下一個儲存單元開始存放:
如:
struct T {
unsigned char a : 1;
unsigned char b : 2;
unsigned : 0;
unsigned c : 3;
};
結構T的成員a和b在一個儲存單元中,c則在另一個儲存單元中。
? 一個位段必須儲存在同一儲存單元中,不能跨兩個單元:
如:
struct T {
unsigned char a : 4;
unsigned char b : 6;
};
結構T的成員a在一個儲存單元中,b則在另一個儲存單元中。
更改C編譯器的預設分配策略
一般地,可以通過下面的兩種方法改變預設的對界條件:
? 使用偽指令#pragma pack ([n])
? 在編譯時間使用命令列參數
#pragma pack ([n])偽指令允許你選擇編譯器為資料分配空間所採取的對界策略,見表2:
表2:更改預設對界條件
在Microsfot Visual C++中,命令列參數/Zp[n]可以改變預設對界條件;在Borland C++ Builder中,命令列參數-a[n]可以改變預設對界條件。n的含義和#pragma pack中的n相同。
例如,在使用了#pragma pack (1)偽指令後,test結構各成員的空間分配情況2所示:
圖2:使用#pragma pack (1)後的結構空間分配
應用執行個體
我們在日常編程工作中,特別是對一些網路事務的處理,經常會同其他人有著各種各樣的協議:如我傳給你20位元組的頭,前4個位元組表示……等等。很多人都是通過指標位移的方法來得到各種資訊,這樣做,不僅編程複雜,而且一旦協議有變化,程式修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來擷取各種資訊。這樣做,不僅簡化了編程,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程式無需修改,省時省力。下面以TCP協議首部為例,說明如何定義協議結構。
TCP協議首部3所示:
圖3:TCP首部
其協議結構定義如下:
struct TCPHEADER {
short SrcPort; // 16位源連接埠號碼
short DstPort; // 16位目的連接埠號碼
int SerialNo; // 32位序號
int AckNo; // 32位確認號
unsigned char HaderLen : 4; // 4位首部長度
unsigned char Reserved1 : 4; // 保留6位中的4位
unsigned char Reserved2 : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位視窗大小
short TcpChkSum; // 16位TCP檢驗和
short UrgentPointer; // 16位緊急指標
};
其協議結構還可以定義為如下的形式:
struct TCPHEADER {
short SrcPort; // 16位源連接埠號碼
short DstPort; // 16位目的連接埠號碼
int SerialNo; // 32位序號
int AckNo; // 32位確認號
unsigned char HaderLen : 4; // 4位首部長度
unsigned char : 0; // 保留6位中的4位
unsigned char Reserved : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位視窗大小
short TcpChkSum; // 16位TCP檢驗和
short UrgentPointer; // 16位緊急指標