透過Bit Field(位欄位)看記憶體

2021-06-08 08:09:18 字數 3776 閱讀 3792

1. bit field 介紹

位字段是乙個signed char、unsigned char、signed int、unsigned int 中一組相鄰的位。位欄位乙個結構宣告建立,該結構宣告為每個字段提供標籤,並決定欄位的寬度(位數)。以 int 型為例,如:

struct field;                   //共佔了8位

struct field field;

field.a = 1; //a = 01 (二進位制)

filed.b = 2; //b = 10 (二進位制)

filed.c = 3; //c = 11 (二進位制)

field.d = 3; //d = 011 (二進位制) 注意:賦值時不要超出欄位的容量,如:d的最大值為7(111)

變數 filed 被儲存在乙個int大小的儲存單元中,這個例子中僅有其中的八位被使用。

宣告的總位數超過乙個unsigned int 的大小,那將會使用下乙個unsigned int 儲存位置。不允許乙個字段跨越兩個unsigned int 之間的邊界。編譯器自動的移位乙個這樣的字段定義,使字段按unsigned int 邊界對齊。發生這種情況時,會在第乙個unsigned int 中留下乙個未命名的洞(unnamed hole )。

可以使用未命名的字段「填充」(pad)未命名的洞。

如果使用乙個寬度為 0 的未命名的字段,則迫使下乙個欄位與下乙個整數對齊。如:

struct field ;
注意:位字段是由機器依賴性的,即將字段放置到 int 中的順序。有些機器是從左向右(先使用高位),另一些機器是從右向左(先使用低位)。

在我的電腦上是從低位到高位的順序放置欄位的。下面的例子足以說明一切。

資料在記憶體中的存放方式是怎樣的?大端(big-endian)還是小端(little-endian)?呼叫函式時資料壓入棧、區域性變數在棧中分配空間的方式是怎樣的?下面的例子足以說明一切。

1)little-endian就是低位位元組排放在記憶體的低位址端,高位位元組排放在記憶體的高位址端。

2)big-endian就是高位位元組排放在記憶體的低位址端,低位位元組排放在記憶體的高位址端。

當然獲得cpu對記憶體採用big-endian還是little-endian的資料讀寫模式還有更簡單的辦法,後面的例子足以說明一切。

2. 下面的例子說明了一切

先看**:

#include int main(void)

field;

typedef struct char char;

int in = 0x13572468; //整型變數賦值,十六進製制

char ch; //char 型位字段變數

field field; //int 型位字段變數

int *add_ch, *add_ch_3; //指標 add_ch指向ch的位址,

//指標 add_ch_3指向比ch的位址小3個位元組單位的位址

ch.a = 15; //0xf

ch.b = 14; //0xe

field.a = 8; //0x8

field.b = 7; //0x7

field.c = 6; //0x6

field.d = 5; //0x5

field.e = 4; //0x4

field.f = 3; //0x3

field.g = 2; //0x2

field.h = 1; //0x1

add_ch = (int *)(&ch); //指標 add_ch指向ch的位址

add_ch_3 = (int *)(&ch - 0x03); //指標 add_ch_3指向比ch的位址小3個位元組單位的位址

printf("\nthe second byte \"24\" of the \"in = 0x13572468\" to char is \"%c\"\n",

*(((char *)(&in) + 0x1))); //列印in第二位元組的內容,如果沒有(char *),則整型指標加一是加四

//位元組單位的位址

printf("sizeof(in) = %d\n", sizeof in);

printf("sizeof(ch) = %d\n", sizeof ch);

printf("sizeof(field) = %d\n\n", sizeof field);

printf("&in = %p\n", &in); //in的位址

printf("&ch = %p\n", &ch); //ch的位址

printf("&field = %p\n\n", &field); //field的位址

printf("in = %#x\n", in);

printf("ch = %#x\n", ch);

printf("field = %#x\n\n", field); //十六進製制列印in、ch、field

printf(" add_ch_3 = (&ch - 0x03) = %p\n", add_ch_3);

printf("*add_ch_3 = *(&ch - 0x3) = %#x\n\n", *add_ch_3);

printf(" add_ch = %p\n", add_ch);

printf("*add_ch = %#x\n", *add_ch);

return 0;

}

再看輸出(除錯模式下):

從輸出結果中畫出記憶體棧,如下圖:

位址為0xbffff705處的位元組為0x24,其正好是字元 '$' 的ascii,因此程式的第一行列印出了 『$』 字元。從此處可以看出cpu對記憶體採用小端的讀寫模式。

把 ch 的位址0xffff703轉化成整型位址型別,列印此位址對應的值為 0x572468ef,其中高3個位元組是 in 值的低3個位元組。

標號 ①、②、③處的值為不確定的。

另外在執行main函式的過程中,按照宣告變數的順序把變數壓入棧中(在棧中依次為變數分配儲存空間)。

通過memory dump可以看出:

第一行中24對應的位元組值為'$'。

通過以上分析,所有問題一目了然。

下面通過更簡單的乙個例子獲得cpu對記憶體資料讀寫模式。

**如下:

#include int main(void)

c; c.a = 1;

if (c.b == 1)

printf("little-endian\n");

else

printf("big-endian\n");

return 0;

}

由於聯合體union的存放順序是所有成員都從低位址開始存放,利用該特性就可以輕鬆地獲得了cpu對記憶體採用little- endian還是big-endian模式讀寫。

談C 生僻知識點 位欄位(bit field)

位欄位允許使用者修改結構體中某個成員變數的特定位數,這種實現對於一般的程式似乎沒有什麼價值,但是對於硬體工程師來說,可以建立與特定硬體裝置上的暫存器對應的資料結構。同時,如果空間寶貴,資料型別儲存資料有空間冗餘,也可以使用位欄位優化。比如,c 中,bool型別僅儲存0或1,但是與char型別一樣佔1...

C C 中的位域 bit field

位域是c c 中常用的資料結構。在某些情況下合理的使用位域可以節省儲存空間,提高執行效率並提高程式的可讀行。按照我以往的程式設計經驗來看,通常以下情況下會優先考慮使用位域。1 有很多的狀態標記,需要集中儲存,比如tcp鏈結 的狀態 2 協議棧相關的資料結構,尤其是底層通訊協議中很多情況使用乙個或者幾...

C struct中的位域 bitfield

結構體的成員可以限制其位域,每個成員可以使用用比位元組還小的取值範圍,下面的結構體s1中,四個成員每個成員都是2bit的值 0 3 整個結構體佔據的空間依然是4個位元組,但是第乙個位元組中表示了四個成員,後續三個位元組沒有用到。struct s1 s1.a 1 s1.b 1 s1.c 1 s1.d ...