1.結構
1.結構聲明和定義及使用
聲明兩種方法:
struct x1{...}; 和 typedef struct{...} x2; //x1表示標籤,實際上可以把struct和x1看成一個整體,表示這是一個結構標籤,相當於整型類型
第一個是聲明一個結構標籤,第二個是聲明類型定義,也是類型重命名。
主要區別 :第二個比較抽象,用戶不必知道它是一個結構,聲明它的實例是不需要使用struct關鍵字
定義
定義方法一:
struct x1 x1;
struct x1 y[20] , *z ;
定義結構體,標籤x1和類型定義使用同樣的名稱是合法的,因爲它們處於獨立的命名空間。
定義方法二:
typedef structx3{...}x3 ; (聲明同時定義)
定義方法三:
x2 x;
x2 y[20] , *z ;
C和C++使用結構的區別:
1,C不能用結構標籤自動生成類型定義名。如struct x{...}; x thestruct ;
2,C模擬C++繼承等面向對象程序設計特性的好方法是:把函數指針直接加入結構中;
成員:
1,結構可以包含指向自己的指針,但是如果使用typedef聲明或定義的結構,可能產生問題。
2,允許結構體組後一個域省略數組大小,柔性數組。
結構體使用之常見錯誤:
1.
extern int f(struct x *p);
結構體使用之疑惑:
1,不能用內建的 “==” 和“!=”操作符比較結構
1)結構體成員涉及內存對齊,對齊時填補空位的是隨機數據,因此不能按字節比較
2)按位域比較的方式,則在處理大結構時可能需要接受大量重複代碼。
3)任何編譯器生成的比較代碼都不能期望在所有情況下都正確比較指針域,一般希望使用strcmp而不是“ == ”。
所以,比較兩個結構,必須自己寫函數按域比較
2,結構傳遞和返回的實現
爲避免結構作爲函數參數傳遞時,把整個結構壓棧而浪費空間,常傳遞一個指向結構的指針。並且對結構體,編譯器將保留一個局部副本。
3,確定域在結構中的字節偏移量,並用名字訪問結構中的域
1,可以用ANSI C在<stddef.h>中定義的offsetof( structs , f )宏計算。
2,自己實現方法:
#define offsetof(type,f) ((size_t)((char *)&((type *)0)->f -(char *)(type *)0))
// 對類型轉換後的空指針(type *)0的減法是爲了確保即使空指針的內部表示不是0的時候,也能正確計算出偏移。
//轉成(char *)指針確保計算出的偏移是字節偏移。
1)創建一個表保存名稱和域偏移量。如結構a的b域(int型)的偏移量:
offsetb = offsetof( struct a , b )
2) 定義structp是結構實例的指針,b的值value 等於絕對地址+偏移量
*(int *)((char *)structp + offsetb)
2.結構填充
爲了能高效訪問內存,編譯器會要求內存對齊。編譯器可能提供某種擴展用於控制結構的填充,如#pragma,但是沒有標準方法。
爲了節省空間,可以把結構中的域從大到小的順序排序。數組成員應該根據它的元素類型大小而不是整個數組的大小。
2.聯合
聯合:本質是一個成員的相互重疊的結構,某一時刻只能用一個成員,也可以從一個成員寫入,從另一個成員讀出,來檢查某種類型的二進制模式。
聯合的大小:最大成員的大小,需要內存對齊。
初始化:C99引入“指定初始式”,可以初始化任意成員。
3.枚舉
枚舉爲整型,枚舉常量爲int型。
優點:
1,自動賦值
2,調試器在檢驗枚舉變量時,可以顯示符號值;
3,它們服從數據塊作用域規則。(當枚舉變量被任意地和其他類型混用,編譯器認爲是壞風格而可產生非致命警告)
缺點:
無法控制枚舉變量的大小。
4.位域
表示該域按位計量的準確大小,只適用於結構和聯合的成員,不適合其他變量。struct record{
char *name;
int refcount : 4;
unsigned dirty : 1;
}
作用:
1,節省存儲空間,特別是對於有很多二進制標誌和其他小成員的結構;
2, 滿足外部要求的存儲佈局。
位域在機器上的分配可能是從左到右,或從右到左。