C語言 自定義類型全面系統理解
一、結構體
結構體是不同類型變量的集合體
1.結構體的聲明
struct Book { char name[20];//名字 int Price;//價格 char Writer[5];//作者 char Time[20];//日期 }; //註意分號不能丟
struct為結構體關鍵字,Book為結構體標簽,中間不同類型的變量為結構體的成員。上述現在隻是定義瞭一個結構體類型struct Book。
局部結構體變量
int main() { struct Book B1; // B1為局部結構體變量 return 0; }
全局結構體變量
struct Book { char name[20]; int Price; char Writer[5]; char Time[20]; }B3,B4,B5; //在結構體類型後可連續定義多個全局結構體變量 struct Book B2; //B2為全局結構體變量 int main() { return 0; }
2.特殊聲明
不完全聲明
//匿名結構體類型--沒有結構體標簽 struct { int a; char b; float c; }x; //這樣的結構體類型必須緊跟著定義結構體變量 //後面不能定義變量
不完全聲明類型隻能在定義使用一次,並且在vs中:
struct { int a; char b; float c; }x; struct { int a; char b; float c; }* ps; int main() { ps=&x; //編譯器默認兩者類型不兼容 //且是錯誤寫法 return 0; }
因此不完全聲明很少使用,不推薦。
3.結構體的自引用
這裡可以用鏈表的實現來理解:
struct Node { int data; struct Node* next; };
這樣就實現瞭自己類型的對象找自己類型對象的方法,這就是結構體的自引用。
4.結構體變量的初始化
以上面struct Book為例:
struct Book { char name[20]; int Price; char Writer[5]; char Time[20]; }; int main() { struct Book B1={"三腳貓",50,“阿裡”,“20081001”}; //初始化要用大括號 return 0; }
嵌套結構體的初始化:
struct Data { int a; char b[6]; }; struct Book { struct Data D; char name[20]; int Price; char Writer[5]; char Time[20]; }; int main() { struct Book B1={{4,"haha"},"三腳貓",50,"阿裡","20081001"}; //大括號裡加大括號 return 0; }
5.結構體內存對齊
計算結構體在內存中的大小
方法:
1. 第一個成員為起始,設從下標為0的地址開始向後存儲。
2. 其他成員變量要對齊到對齊數的整數倍的地址處。 對齊數 = 編譯器默認的一個對齊數與 該成員大小的較小值。 VS中默認的值為8
3. 結構體總大小為所有成員對齊數中最大對齊數的整數倍。
4. 如果嵌套瞭結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
舉例說明:
#include <stdio.h> struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
分析:
嵌套結構體
#include <stdio.h> struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; struct S1 s1; int i; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
分析:
內存對齊的存在,在平臺和性能兩方面,可以使訪問空間更加高效,用空間換取時間。
讓占用空間小的成員盡量集中在一起。
6.修改默認對齊數
#pragma pack(1) //修改默認對齊數為1 //一般修改的對齊數為2^n
舉例說明:
#include <stdio.h> #pragma pack(1) //修改為1相當於取消瞭對齊,沒有優化,在實際應用中很少用 struct S1 { char c1; int i; char c2; }; int main() { printf("%d\n", sizeof(struct S1)); // 6 return 0; }
當默認對齊數被修改後,每個類型的對齊數都變為1,整體的最大對齊數也為1(相當於沒有對齊),整體大小是1的倍數,則1+4+1=6。
7.結構體傳參
當一個函數涉及到結構體時,最好用傳址調用:
struct S { int data[1000]; int num; }; struct S s = {{1,2,3,4}, 1000}; //結構體傳參 void print1(struct S s) { printf("%d\n", s.num); } //結構體地址傳參 void print2(struct S* ps) { printf("%d\n", ps->num); } int main() { print1(s); //傳結構體 print2(&s); //傳地址 return 0; }
傳址調用原因:
1.函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。
2.如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。
二、位段
位段的聲明和結構是類似的,有兩個不同:
1.位段的成員必須是整型。
2.位段的成員名後邊有一個冒號和一個數字。
舉例:
struct S { int _a:2; int _b:5; int _c:20; int _d:25; };
此時S就是一個位段類型
他的大小為8
printf("%d\n", sizeof(struct S));
分析:
下面我們來分析位段在內存中的存儲:
註:若初始化的值大於給其指定的空間,則先會發生截斷(斷左取右),再進行存儲
位段是根據實際需求來進行開辟空間,目的是為瞭節省空間提高效率。
跨平臺問題:
1. int 位段被當成有符號數還是無符號數是不確定的。
2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機 器會出問題。
3. 位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。(取決於編譯器)
4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納於第一個位段剩餘的位時,是 舍棄剩餘的位還是利用,這是不確定的。
三、枚舉
就是把可能的取值一一列舉
枚舉類型的定義:
例子:
enum Day //星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun };
enum Day就是一個枚舉類型,{}中的內容是枚舉類型的可能取值,也叫枚舉常量。
枚舉常量是有值的,默認從0開始,依次遞增。
int main() { enum Day d=Mon; //定義一個變量,賦予{}內可能的取值。 return 0; }
在定義的時候也可以賦初值,後面的常量依次遞增一
枚舉的優點
1. 增加代碼的可讀性和可維護性
2. 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹。
3. 防止瞭命名污染(封裝)
4. 便於調試
5. 使用方便,一次可以定義多個常量
註:最好用枚舉常量給枚舉變量賦值,才不會出現類型上的差異。
舉例:
當用cpp來運行程序時會報錯,因為cpp對代碼的格式會更加嚴格
而c就可以運行過去
養成良好的代碼風格,做到認真嚴謹。
四、聯合
聯合也是一種特殊的自定義類型
1.聯合類型的定義
//聯合類型的聲明 union Un { char c; int i; }; //聯合變量的定義 union Un un; //計算連個變量的大小 printf("%d\n", sizeof(un));
2.聯合的特點
聯合的成員是共用同一塊內存空間的,這樣一個聯合變量的大小,至少是最大成員的大小。
union Un { int i; char c; }; union Un un; int main() { printf("%d\n", &(un.i)); printf("%d\n", &(un.c)); return 0; }
共用一塊空間,起始地址相同。
使用案例:
union Un { int i; char c; }; union Un un; int main() { //printf("%d\n", &(un.i)); //printf("%d\n", &(un.c)); un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i); return 0; }
分析:
3.聯合大小的計算
聯合的大小至少是最大成員的大小。
當最大成員大小不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍。
舉例:
union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; //下面輸出的結果是什麼? int main() { printf("%d\n", sizeof(union Un1)); printf("%d\n", sizeof(union Un2)); return 0; }
分析:
到此這篇關於C語言 自定義類型全面理解的文章就介紹到這瞭,更多相關C語言 自定義類型內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- C語言自定義類型詳解(結構體、枚舉、聯合體和位段)
- C語言自定義類型的保姆級講解
- C語言自定義數據類型的結構體、枚舉和聯合詳解
- C++如何計算結構體與對象的大小
- 一篇文章帶你瞭解C語言:入門基礎(2)