前言:
c語言中中自定義類型不僅有結構體,還有枚舉、聯合體等類型,上一期我們詳細講解了結構體的初始化,使用,傳參和內存對齊等知識,這一期我們來介紹c語言中的其他自定義類型枚舉和聯合體的知識。
1.位段
? ? 在講枚舉,聯合體之前,我們補充上一期結構體剩下的一點知識——位段。
1.1什么是位段
位段的聲明和結構是類似的,有兩個不同:
1.位段的成員必須是 int、unsigned int 或signed int 。
2.位段的成員名后邊有一個冒號和一個數字。
比如:
struct A
{int _a:2;int _b:5;int _c:10;int _d:30;
};
A就是一個位段類型。
那位段A的大小是多少? ?
printf("%d\n", sizeof(struct A));
這就不得不介紹以上的代碼是什么意思了,_a后面的2表示我們只給_a?變量分配兩個比特位的空間,以此類推,后面的5、10、30都是給各自變量分配了該數量的比特位的空間,這是為什么呢?為什么要給一個變量這么小的空間呢?因為有時我們發現有的變量只固定表示一些很小的數值,如_a變量,我們如果只需要它表示0-3的值,給它分配兩個比特位是完全夠的,所以使用位段是為了節省空間的做法,在某些變量只表示固定范圍的數值時,我們就用位段限制它的空間,盡可能去節省空間,那么我們來看這個結構體的空間大小吧:
四個int類型占8個字節,平均占2個字節,在不超出數值表示范圍的情況下,我們用位段省下了一半的空間。
1.2 位段的內存分配?
? ? 既然位段能節省空間,我們就不得不解釋位段是如何分配內存的:
1. 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)類型
2. 位段的空間上是按照需要以4個字節( int )或者1個字節( char )的方式來開辟的。
3. 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段。
我們來看一段代碼:
//一個例子
struct S
{char a:3;char b:4;char c:5;char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
我們給a、b、c、d、四個變量分別分配了3,4,5,4個比特位,3+4+5+4=16,它們加起來占16個比特位,是不是意味著S占16÷8=2個字節呢?我們計算一下S所占的空間:
出乎我們意料,它占了三個字節,這是因為位段的存儲規則是不確定的。
如果這個結構體占三個字節,那么它內部是這樣存儲的:
因為a占3個比特位,b占四個比特位,加起來不超過一個字節,所以它們被放在同一個字節內,而c占5個比特位,字節1空間不夠,所以被放在了字節2,此時字節2還剩3個比特位,d占四個比特位,顯然字節2放不下,又開辟了字節3把d放在里面,剩下一個字節的空間就是這樣丟失的。
?1.3 位段的跨平臺問題
1. int 位段被當成有符號數還是無符號數是不確定的。
2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機 器會出問題。
3. 位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。
4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是 舍棄剩余的位還是利用,這是不確定的。
總結:
跟結構相比,位段可以達到同樣的效果,并且可以很好的節省空間,但是有跨平臺的問題存在。
2.枚舉
? 枚舉也是c語言中自定義類型的一種,那么枚舉是什么呢?
枚舉顧名思義就是一一列舉。
把可能的取值一一列舉。
比如我們現實生活中:
一周的星期一到星期日是有限的7天,可以一一列舉。
性別有:男、女、保密,也可以一一列舉。
月份有12個月,也可以一一列舉。
這里就可以使用枚舉了。
2.1枚舉類型的定義
相比于結構體的關鍵字為struct,枚舉也有自己的關鍵字:enum,了解了它的關鍵字,我們來看枚舉的應用實例:如我們要表示一周七天,我們要表示性別,三原色
enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
enum Sex//性別
{MALE,FEMALE,SECRET
};
enum Color//顏色
{RED,GREEN,BLUE
};
以上定義的 enum Day , enum Sex , enum Color 都是枚舉類型。
{}中的內容是枚舉類型的可能取值,也叫枚舉常量 。
這些可能取值都是有值的,默認從0開始,依次遞增1,什么意思呢?如我們的Color類型里面的從頭開始為RED,那么它的值就是0,相應的,GREED的值為1,BLUE的值為2,當然在聲明枚舉類型的時候也可以賦初值。
enum Color//顏色
{RED=1,GREEN=2,BLUE=4
};
2.2枚舉類型的優點?
為什么要使用枚舉?
我們可以使用 #define 定義常量,為什么非要使用枚舉?
枚舉的優點:
1. 增加代碼的可讀性和可維護性
2. 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹。
3. 便于調試
4. 使用方便,一次可以定義多個常量
3.聯合體(共用體)
3.1聯合類型的定義
聯合也是一種特殊的自定義類型 這種類型定義的變量也包含一系列的成員,特征是這些成員公用同一塊空間(所以聯合也叫共用體),關鍵字為union。
比如:
//聯合類型的聲明
union Un
{char c;int i;
};
//聯合變量的定義
union Un un;
//計算連個變量的大小
printf("%d\n", sizeof(un));
3.2聯合體的特點?
?聯合的成員是共用同一塊內存空間的,這樣一個聯合變量的大小,至少是最大成員的大小(因為聯 合至少得有能力保存最大的那個成員)。
問:
union Un
{int i;char c;
};
union Un un;
// 下面輸出的結果是一樣的嗎?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
首先我們來分析一下,因為我們聯合體的特點是變量之間共用一塊空間,所以i的地址和c的地址是同一塊,那么它們&i和&c的結果是一樣的:
很顯然,我們的分析是正確的。
3.3聯合大小的計算
聯合體占多少空間,有兩個規則:
1.聯合的大小至少是最大成員的大小。
2.當最大成員大小不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍。
比如:
union Un1
{char c[5];int i;
};
union Un2
{short c[7];int i;
};
//下面輸出的結果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
從Un1開始,它的內部定義了一個5個元素的char類型的數組,長度為5個字節,那是否說明它的內存就是5個字節呢?我們回到上面內存規則的第二條—— 當最大成員大小不是最大對齊數的整數倍的時候,就要對齊到最大對齊數的整數倍,很顯然5不是4的整數倍,所以我們擴展到了8,那么8就是它所占的字節數:
結果完全正確,那么Un2呢,short類型占2個字節,我們定義了一個7個變量的short類型的數組,占14個字節,又創建了一個int類型的變量,考慮到聯合體內存共用的特性,結果肯定不為14+4,我們要考的還是第二條對齊的規則,14不是4的整數倍,由14擴展到了16:
怎么樣,是不是對聯合體內存的規則有一定的了解了呢。
3.4聯合體的實際應用?
? ? 聯合體有很多作用,我們之前講過大小端字節序的概念,還講了設計一個程序判斷當前環境是大端還是小端,學了聯合體之后,我們使用聯合體也能設計這樣的程序且簡單,清晰明了:
union Un
{char c;int i;
};
int main()
{union Un un = { 0 };un.i = 1;if (un.c == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}
我們當前是小端字節序,來看結果吧:
?
是不是很神奇呢。
到這里我們自定義類型的的內容就到此結束了,各位友友讀到這里留下寶貴的三連和評論吧,有不足之處望各位佬佬私信和我交流!!!?
?
?
?