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