/x86/Debian GNU/Linux/gcc

1 聯合體

(1)聯合體機制

聯合體的所有成員引用的是記憶體中的相同地址。訪問聯合的不同成員時,會根據此成員的型別去訪問對應的位元組,並根據此成員的型別去解釋這些位元組對應位的含義(表示int還是float)。

聯合體初始化時,初始化的是聯合的第一個成員(所以型別應該與聯合的第一個成員相同,否則會發生型別轉換),而且初始值必須位於一對大括號內。

(2)用聯合體測平臺數據儲存是大端還是小端

要想知道平臺採用的大端還是小端的方式儲存資料,可以看.c檔案對應的目標檔案或者可執行檔案。在知道系統採用的儲存方式後,可以反推記憶體地址採用的高地址還是低地址來表示。

//When this paltom uses litte address to be many bytes's address
//Test platom is litte or bit endian
void test_endian(void)
{
	printf("\n--------------test_endian function------------------\n");
	union one_test{
		unsigned int i;
		unsigned char ch;
	}my_test={1};
	
	printf("address of i = %p\naddress of ch = %p\n\n", &my_test.i, &my_test.ch);
	printf("ch = %d\nhex dump of ch = 0x%x\n", my_test.ch, my_test.ch);
	
	if(my_test.ch == 1){
		printf("This platom uses litte endian\n");
	}else{
		printf("This platom uses big endian\n");
	}
}

聯合體my_test中的ich的記憶體地址相同。不同的是,訪問my_test.i時會讀sizeof(unsigned int)位元組內容並按照int型別解釋這些位;訪問my_test.ch時會讀一個位元組內容並按照char型別(實質是int)解釋這些位。

my_test經初始化後,my_test.chmy_test.i的第一個位元組(表示整個變數的地址)內容相同。如果系統採用低地址來作為一個型別的地址int4個位元組,用4個位元組中的最低地址表示int變數的地址),那麼以上程式就能夠測試出系統採用的大端還是小端方式儲存資料。如果my_test.ch值為1則表示系統採用小端方式儲存資料,否則為大端方式。如果系統採用高地址來作為一個型別的地址,那麼my_test.ch值為1則表示系統採用大端方式儲存資料,否則為小端方式。

將這段程式碼放在main函式中,某次執行的結果如下:

--------------test_endian function------------------

address of i = 0xbff03cdc

address of ch = 0xbff03cdc

ch = 1

hex dump of ch = 0x1

This platom uses litte endian

已經在某.c檔案的目標檔案中得知/x86/Debian GNU/Linux平臺採用的小端方式儲存資料,故而得它用的低地址表示記憶體段地址。

2 位段

(1)結構體成員的儲存地址

陣列元素的地址按照下標連續遞增。結構體內的成員的地址雖不連續,但會按照成員的定義順序各成員的地址會由低到高。

(2)位段的儲存

//Test Bit-field's memory layout
void test_bit_field_layout(void)
{
	printf("\n--------------test_bit_field_layout---------------\n");
	union two_test{
		struct bit_field{
			unsigned int one	:8;
			unsigned int two	:1;
			unsigned int three	:3;
			unsigned int 		:4;
			unsigned int four	:1;
			unsigned int five	:8;
		}my_bit_field;
		unsigned char byte[4];
	}my_test={{2, 1, 4, 1, 16}};

	printf("value of byte:\nbyte[0] = %d\tbyte[1] = %d\tbyte[2] = %d\tbyte[3] = %d\n",		\
		my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]);
	printf("\nhex dump of byte:\nbyte[0] = 0x%x\tbyte[1] = 0x%x\tbyte[2] = 0x%x\tbyte[3] = 0x%x\n",	\
		my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]);
}

將這個函式放在main函式中,某次執行得到的結果如下:

--------------test_bit_field_layout---------------

value of byte:

byte[0] = 2byte[1] = 9byte[2] = 33byte[3] = 0

hex dump of byte:

byte[0] = 0x2    byte[1] = 0x9    byte[2] = 0x21 byte[3] = 0x0

分析各位段儲存的資料(以小端的方式儲存):

如果將未命名的欄位unsignedint         :4;遮蔽,則程式執行如下:

--------------test_bit_field_layout---------------

value of byte:

byte[0] = 2byte[1] = 25byte[2] = 2byte[3] = 0

hex dump of byte:

byte[0] = 0x2    byte[1] = 0x19 byte[2] = 0x2 byte[3] = 0x0

則各位段儲存的資料(以小端的方式儲存):

與一般結構體一樣,為了訪問的效率,編譯器可能會在兩個位段間及結構體末尾加入填充位

關於如何排列Bit-field在C標準中沒有詳細的規定,這跟Byte Order、Bit Order、對齊等問題都有關,不同的平臺和編譯器可能會排列得很不一樣,要編寫可移植的程式碼就不能假定Bit-field是按某一種固定方式排列的。同時Bit-field在驅動程式中是很有用的,因為經常需要單獨操作裝置暫存器中的一個或幾個bit,但一定要小心使用,首先弄清楚每個Bit-field和實際bit的對應關係

CNote Over.

.