C和指標之結構體和聯合體
阿新 • • 發佈:2018-11-04
1、結構體基礎知識
聚合資料型別(aggregate data type)能夠同時儲存超過一個的單獨資料。C語言提供了兩種型別的聚合資料結構,陣列和結構體。
陣列是相同型別的資料元素的集合,它的每個元素都是通過下標引用或者指標間接訪問來選擇的;結構也是一些值的集合,這些值稱為它的成員(member),結構體的每個成員可以是不同型別的資料,要訪問結構體中的資料,是通過成員名來訪問的。
結構變數屬於標量型別,所以你可以像對待其他標量型別一樣執行相同型別的操作。結構體可以作為傳遞給函式的引數,也可以作為返回值從函式返回,相同型別的結構體變數之間可以相互賦值。你也可以申明指向結構體的指標,取一個結構體變數的地址,還可以宣告結構體陣列。
1)宣告結構體
結構體記憶體分配遵循邊界對齊原則。
1)普通資料成員對齊原則:第一個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始(比如int在32位機為4位元組,則要從4的整數倍地址開始儲存)。
2)結構體成員對齊原則:如果一個結構裡有某些結構體成員,則該結構體成員要從其內部最大元素大小的整數倍地址開始儲存。
3)結構體大小對齊原則:結構體大小也就是sizeof的結果,必須是其內部成員中最大的對齊引數的整數倍,不足的要補齊。
以如上結構體ST_TEST為例:
成員a、c為char型,各佔一個位元組,b為int型,佔4個位元組,如果你認為結構體ST_TEST佔了6個位元組的空間,那麼你就大錯特錯了。因為沒有遵循結構體邊界對齊原則。下面我們來分析一下。按照邊界對齊原則,結構體成員要從內部最大元素大小的整數倍地址開始儲存,例子結構體中的成員最大的是int型的b,佔4個位元組,所以,char a和char c都要佔4個位元組,第一個位元組為資料a、c,後三個為空。所以ST_TEST結構體共佔了12個位元組的空間。
如果將結構體改為如下形式:
typedef struct2)結構體成員的訪問 點操作符(.):對結構體成員進行直接訪問。點操作符接受兩個運算元,左運算元就是結構變數的名字,右運算元就是需要訪問的成員的名字。 箭頭操作符(->):對結構體成員進行間接訪問。和點操作符一樣,箭頭操作符接受兩個運算元,但是左運算元必須是一個指向結構的指標,右運算元也是需要訪問的成員的名字。 2、結構體的儲存分配tagTEST_DATA { int a; int *p; char b; char c[]; }ST_TEST_DATA;
typedef struct tagST_TEST { char a; int b; char c }ST_TEST;
typedef struct此時,int型的b佔4個位元組,char a,char c各佔一個,但是在其後要補兩個空位元組(此時的對齊原則按照int型的4位元組計算,但是,兩個char連在一起,可以算一個整體,共佔4個位元組),此時結構體ST_TEST1總共佔了8個位元組的空間,比ST_TEST少了4個位元組。 以後在定義結構體時要考慮邊界對齊原則,可以通過改變成員的先後順序來達到節省儲存空間的目的。但是這樣做有時有會破壞程式的可維護性和可讀性,所以多做程式設計練習,在儲存空間和可讀性中找到平衡,提高自己的程式設計水平。 sizeof操作符能夠得出一個結構體的整體長度,包括因邊界對齊原則而跳過的那些位元組。 offsetof巨集則能夠得出某個成員的實際位置,需要考慮邊界對齊因素。offsetof(type, member)(定義於stddef.h)type是結構體的型別,member是需要的成員名,返回值為size_t型別的指定成員開始儲存的位置距離結構體開始儲存的位置的偏移的位元組數。如offsetof(struct ST_TEST, b),返回值為4。 3、作為函式引數的結構 結構體變數是一個標量,它可以用於其他標量可以使用的任何場所。因此把結構體作為一個引數傳遞給函式是合法的。 將一個結構體直接作為引數傳遞給函式是一種及其低效的做法,因為C語言的引數傳值呼叫方式要求把引數的一份拷貝傳遞給函式,要想把結構體直接傳遞給函式,就需要把整個結構體都複製到堆疊中,這顯然非常浪費儲存空間。所以傳遞一個指向結構的指標給函式,效率要高很多。在函式中要訪問結構成員只需要用間接訪問的方式就可以了。 4、聯合體(共用體) 在結構體中,結構的各成員順序排列儲存,每個成員都有自己獨立的儲存位置。聯合(union)變數的所有成員共享同片儲存區/記憶體。因此聯合變數每個時刻裡只能儲存它的某一個成員的值。 聯合變數也可以在定義時直接初始化,但這個初始化只能對第一個成員進行。 聯合體的主要特徵: union中可以定義多個成員,union的大小由最大的成員的大小決定; union成員共享同一塊大小的記憶體,一次只能使用其中的一個成員; 對union某一個成員賦值,會覆蓋其他成員的值(但前提是成員所佔位元組數相同,當成員所佔位元組數不同時只會覆蓋相應位元組上的值,比如對char成員賦值就不會把整個int成員覆蓋掉,因為char只佔一個位元組,而int佔四個位元組); union量的存放順序是所有成員都從低地址開始存放的。tagST_TEST1 { int b; char a; char c; }ST_TEST1;