在火爐做網(wǎng)站公園坐什么車網(wǎng)絡(luò)營銷圖片
自定義類型(結(jié)構(gòu)體、聯(lián)合體、枚舉)
- 一、結(jié)構(gòu)體
- (一)結(jié)構(gòu)體的內(nèi)存對齊
- 1、結(jié)構(gòu)體內(nèi)存對齊規(guī)則
- (1)引子
- (2)offsetof 宏函數(shù)
- (3)內(nèi)存對齊原理
- (4)自定義默認(rèn)對齊數(shù)
- 2、為什么要內(nèi)存對齊
- (1)性能原因
- (2) 平臺原因
- (二)結(jié)構(gòu)體實(shí)現(xiàn)位段
- 1、位段的定義
- 2、位段的儲存
- 3、位段的不可跨平臺性
- 4、位段的應(yīng)用
- 二、枚舉
- (一)枚舉的定義
- (二)枚舉的優(yōu)點(diǎn)
- 三、聯(lián)合體
- (一)聯(lián)合體的儲存
- (二)聯(lián)合體的應(yīng)用
- 四、結(jié)束語
一、結(jié)構(gòu)體
(一)結(jié)構(gòu)體的內(nèi)存對齊
1、結(jié)構(gòu)體內(nèi)存對齊規(guī)則
(1)引子
結(jié)構(gòu)體占多少內(nèi)存呢,是不是各個(gè)變量的內(nèi)存之和呢,先用一段代碼來驗(yàn)證我們的猜想吧。下面代碼中,如果我們猜想正確,那么打印結(jié)果就應(yīng)該是三個(gè)變量的字節(jié)數(shù)之和,答案為6.
(2)offsetof 宏函數(shù)
offsetof (type,member) 返回成員變量number距離結(jié)構(gòu)體首地址的偏移量(單位是字節(jié))
我們對結(jié)構(gòu)體S的a, b, c,分別查看他們的偏移量。
根據(jù)這個(gè)結(jié)果,我們可以畫出下面的內(nèi)存圖解。
(3)內(nèi)存對齊原理
1、結(jié)構(gòu)體的第一個(gè)成員變量對齊到和結(jié)構(gòu)體首地址偏移量為0的地址處
2、其它成員變量要對齊到對齊數(shù)的整數(shù)地址處。
對齊數(shù) = 編譯器的默認(rèn)對齊數(shù) 和 該結(jié)構(gòu)體成員變量的大小 中的較小值
3、結(jié)構(gòu)體大小為最大對齊數(shù)的整數(shù)倍。
4、如果結(jié)構(gòu)體嵌套結(jié)構(gòu)體,嵌套的結(jié)構(gòu)體變量要對齊到自己成員變量中最大對齊數(shù)整數(shù)倍的地址處。結(jié)構(gòu)體大小就是所有成員(包含結(jié)構(gòu)體中的成員)中最大對齊數(shù)的整數(shù)倍。
求下面代碼的輸出結(jié)果。
struct S1 {double a;char b;int c;
};struct S2 {char a;struct S1 b;double c;
};int main() {printf("%zd", sizeof(S2));return 0;
}
要求結(jié)構(gòu)體S2的大小,我們首先要求結(jié)構(gòu)體S1的大小,分析如下。
接著再求結(jié)構(gòu)體S2的大小
分析結(jié)果為32和我們預(yù)料的一致。
(4)自定義默認(rèn)對齊數(shù)
使用 #pragma pack(int val)可以設(shè)置默認(rèn)對齊數(shù)。
例如:
#pragma pack(1)//將默認(rèn)對齊數(shù)設(shè)置為1struct S {char c1;int n;char c2;
}#pragma pack() //重置默認(rèn)對齊數(shù)
2、為什么要內(nèi)存對齊
所以說不僅步驟麻煩,還消耗了內(nèi)存,為什么還要內(nèi)存對齊呢。用空間換時(shí)間!
(1)性能原因
為了訪問未對齊的數(shù)據(jù),處理器需要訪問多次,而處理對齊的數(shù)據(jù),處理器訪問次數(shù)減少。
struct S {char a;int b;
};
在32位平臺中,我們一次可以訪問四個(gè)字節(jié)。下面我們訪問變量 b 并用內(nèi)存對齊和非內(nèi)存對齊來對比
內(nèi)存對齊
非內(nèi)存對齊
通過非內(nèi)存對齊,發(fā)現(xiàn)我們起碼要通過兩次才能拿到 int 成員變量。
(2) 平臺原因
不是所有平臺都能訪問任意地址上的任一數(shù)據(jù)。
(二)結(jié)構(gòu)體實(shí)現(xiàn)位段
1、位段的定義
位段中的位指的是二進(jìn)制位,和結(jié)構(gòu)體類似,有兩點(diǎn)不同
1、位段的類型只能是 int 、signed int、unsigned int 或者是char(整形家族),C99中引入了其它類型。
2、位段后面比較加冒號和數(shù)字,表示為該變量分配多少個(gè)比特位的空間。
如下:
struct S {int a : 2;int b : 6;int c : 4;int c : 4;
};
2、位段的儲存
位段也有自己的儲存方式,我們分析下面一段代碼進(jìn)行體會
struct S {int a : 3;int b : 4;int c : 5;int d : 4;
};int main() {printf("%d", sizeof(struct S));return 0;
}
注意:
位段在初始化變量時(shí)候不可以用scanf,因?yàn)?amp;符號是以一個(gè)字節(jié)為單位,顯然如果一個(gè)字節(jié)里面有多個(gè)變量的話,會引發(fā)錯(cuò)誤。
3、位段的不可跨平臺性
1、數(shù)據(jù)類型解釋的不確定性:
有符號與無符號:位段中的int類型成員是否被當(dāng)作有符號數(shù)還是無符號數(shù),在不同的編譯器和平臺上可能存在差異。
位數(shù)限制:位段中成員的最大位數(shù)在不同的編譯器和平臺上可能有所不同。例如,在16位機(jī)器上,位段的最大位數(shù)可能為16,而在32位機(jī)器上可能為32。
2、內(nèi)存分配方式的不確定性:
分配方向:位段成員在內(nèi)存中的分配方向(從左到右或從右到左)在C語言標(biāo)準(zhǔn)中并未明確定義,這可能導(dǎo)致在不同的編譯器和平臺上出現(xiàn)不同的內(nèi)存布局。
空間利用:當(dāng)一個(gè)結(jié)構(gòu)體包含多個(gè)位段成員,且后一個(gè)成員無法完全容納在前一個(gè)成員剩余的位中時(shí),是舍棄剩余位還是嘗試?yán)眠@些位,也是不確定的。
4、位段的應(yīng)用
網(wǎng)絡(luò)協(xié)議中用來包裝IP數(shù)據(jù)報(bào)(就好像外賣一樣,需要填寫目的地的信息),而位段進(jìn)行封裝就會使得包裹更加便捷。
二、枚舉
(一)枚舉的定義
我們可以把生活中一些事物所對應(yīng)的情況一一列舉處來,就是枚舉。
如下,其中enum就是枚舉類型,{}里的內(nèi)容就是枚舉常量。
變量中的枚舉常量默認(rèn)賦初始值為0 1 2 3 … 也可以自定義賦值。自定義賦值時(shí),可以選擇性賦值,沒有賦值的會序列化自動(dòng)賦值。
(二)枚舉的優(yōu)點(diǎn)
枚舉的優(yōu)點(diǎn)用以下代碼可以充分體現(xiàn)。
enum Option
{EXIT,//0ADD,//1SUB,MUL,DIV
};void menu()
{printf("**********************************\n");printf("****** 1. add 2. sub ******\n");printf("****** 3. mul 4. div ******\n");printf("****** 0. exit ******\n");printf("**********************************\n");
}int main()
{int input = 0;do{menu();printf("請選擇:>");scanf("%d", &input);switch (input){case SUB://break;case ADD:break;case MUL:break;case DIV:break;case EXIT:printf("退出\n");break;default:printf("選擇錯(cuò)誤,重新選擇\n");break;}} while (input);return 0;
}
三、聯(lián)合體
(一)聯(lián)合體的儲存
我們直接將聯(lián)合體的儲存是因?yàn)樗徒Y(jié)構(gòu)體上面就一個(gè)本質(zhì)差別:聯(lián)合體成員公用同一塊空間(所以聯(lián)合也叫共用體)。
規(guī)則:
1、聯(lián)合的大小至少是最大成員的大小。
2、當(dāng)最大成員大小不是最大對齊數(shù)的整數(shù)倍的時(shí)候,就要對齊到最大對齊數(shù)的整數(shù)倍。
(二)聯(lián)合體的應(yīng)用
聯(lián)合體和結(jié)構(gòu)體嵌套使用,可以節(jié)省很多內(nèi)存:
例如我們需要使用該結(jié)構(gòu)體,如果前面三個(gè)公共屬性必選,而特征屬性非必選,我們可以設(shè)置下面的代碼來減少結(jié)構(gòu)體的內(nèi)存:
四、結(jié)束語
到此這篇文章就告一段落了,小編還會繼續(xù)用心對待每一篇文章,與大家一起進(jìn)步,非常有幸能得到大家的支持!