??前言
聯(lián)合體(union
)是允許一個(gè)變量通過不同的接口訪問內(nèi)存的一種數(shù)據(jù)類型,表示一個(gè)變量可以存儲(chǔ)不同類型的值,而枚舉是使用enum
關(guān)鍵字定義一組相關(guān)且互斥的整形常量集合。本章阿森將和你學(xué)習(xí)聯(lián)合體類型的聲明,特點(diǎn),有關(guān)大小的計(jì)算,還有枚舉類型的聲明,優(yōu)點(diǎn)和使用。文章干貨滿滿!學(xué)習(xí)起來吧??!
?? 聯(lián)合體類型的聲明
同結(jié)構(gòu)體一樣,聲明結(jié)構(gòu)體類型需要使用struct
關(guān)鍵字,聯(lián)合體則用union
關(guān)鍵字。
- 包含對象名的聲明方式:
union 聯(lián)合體名
{
類型 成員1;
類型 成員2;
...
類型 成員n;
}對象名;
- 代碼理解:
#include <stdio.h>
union S
{
char c;
int a;
}s1;
int main()
{
s1.c = 'a';
printf("%c\n", s1.c);
s1.a = 10;
printf("%d\n", s1.a);
return 0;
}
代碼運(yùn)行:
- 不包含對象名的聲明格式:
union 類型名
{
類型 成員1;
類型 成員2;
...
類型 成員n;
};
- 代碼實(shí)現(xiàn):
#include <stdio.h>
union S
{
char c;
int a;
};
int main()
{
union S s2;
s2.c = 'b';
printf("%c\n", s2.c);
s2.a = 20;
printf("%d\n", s2.a);
return 0;
}
運(yùn)行:
??聯(lián)合體的特點(diǎn)
- 編譯器只為最?的成員分配?夠的內(nèi)存空間。聯(lián)合體的特點(diǎn)是所有成員共?同?塊內(nèi)存空間。所以聯(lián)合體也叫:共?體。
例如:
union u
{
char c;
int u;
};
int main()
{
union u uu;
printf("聯(lián)合體uu的大小為%zd\n", sizeof(uu));
printf(" (uu)地址為%p\n", &uu);
printf("&(uu.c)地址為%p\n", &(uu.c));
printf("&(uu.u)地址為%p\n", &(uu.u));
return 0;
}
輸出:
圖解:
- 聯(lián)合的成員是共?同?塊內(nèi)存空間的,這樣?個(gè)聯(lián)合變量的??,?少是最?成員的??(因?yàn)槁?lián)合?少得有能?保存最?的那個(gè)成員)。
//聯(lián)合類型的聲明
union u
{
char c;
int i;
};
int main()
{
//聯(lián)合變量的定義
union u uu = { 0 };
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", uu.i);
return 0;
}
輸出:
圖解:union
定義了int
和char
兩個(gè)成員,共享同一塊內(nèi)存空間,int
類型占4
個(gè)字節(jié),低地址在前,高地址在后,char
類型只占1
個(gè)字節(jié),存儲(chǔ)在int
的低地址字節(jié)。當(dāng)執(zhí)行:uu.i = 0x11223344時(shí),此時(shí)int的4個(gè)字節(jié)分別存儲(chǔ)如圖,然后執(zhí)行: uu.c = 0x55,由于VS是小端存儲(chǔ),低字節(jié)放在低地址處,char
只占1
個(gè)字節(jié),它會(huì)覆蓋int
低地址的那個(gè)字節(jié)。所以int
原來低地址字節(jié)0x44
被覆蓋為0x55
。
??相同成員的結(jié)構(gòu)體和聯(lián)合體對?
結(jié)構(gòu)體和聯(lián)合體的主要區(qū)別在于:
- 結(jié)構(gòu)體中每個(gè)成員占用自己獨(dú)立的內(nèi)存空間,可以同時(shí)訪問每個(gè)成員。
- 聯(lián)合體中所有成員共享同一塊內(nèi)存空間,只能同時(shí)訪問其中一個(gè)成員。
-
內(nèi)存布局:
結(jié)構(gòu)體中每個(gè)成員都有固定的偏移地址,占用獨(dú)立的內(nèi)存空間。
聯(lián)合體中所有成員共享同一塊內(nèi)存,沒有偏移地址,只能使用一個(gè)成員。 -
訪問成員:
結(jié)構(gòu)體可以同時(shí)讀取各個(gè)成員的值。
聯(lián)合體只能訪問當(dāng)前使用的成員,其他成員的值將被覆蓋。 -
大?。?br> 結(jié)構(gòu)體的大小是所有成員大小的和。
聯(lián)合體的大小至少是最大成員的大小。
- 結(jié)構(gòu)體:
struct S
{
char c;
int i;
};
struct S s = {0};
- 聯(lián)合體:
union u
{
char c;
int i;
};
union u uu = { 0 };
圖解對比:
結(jié)構(gòu)體S
占用char + int+有可能開辟浪費(fèi)的空間
大小的內(nèi)存,可以獨(dú)立訪問c
和i
,聯(lián)合體u
只占用int
大小的內(nèi)存,訪問c
或i
時(shí)值會(huì)覆蓋,結(jié)構(gòu)體各成員獨(dú)立,聯(lián)合體成員共享同一內(nèi)存空間。
??聯(lián)合體??的計(jì)算
點(diǎn)擊可以查看結(jié)構(gòu)體的內(nèi)存對齊規(guī)則——>【C語言】自定義類型:結(jié)構(gòu)體深入解析(二)結(jié)構(gòu)體內(nèi)存對齊&&宏offsetof計(jì)算偏移量&&結(jié)構(gòu)體傳參
聯(lián)合體大小計(jì)算規(guī)則:
- 聯(lián)合的???少是最?成員的??。
- 當(dāng)最?成員??不是最?對?數(shù)的整數(shù)倍的時(shí)候,就要對?到最?對?數(shù)的整數(shù)倍。
- 來代碼理解:
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
printf("%zd\n", sizeof(union Un1));//8
printf("%zd\n", sizeof(union Un2));//16
return 0;
}
運(yùn)行:
圖解分析:
首先看union Un1
如果聯(lián)合體的大小是最大成員的最大成員的的大小,在聯(lián)合體union Un1
中,char[5]
的大小理應(yīng)是5
,那計(jì)算的結(jié)果不是5
。為什么是8
呢?這是因?yàn)樗瓿闪藢R的操作,如果是數(shù)組,是按元素類型大小來算他的對齊數(shù)的。char
元素的類型大小是1
,VS
默認(rèn)對齊數(shù)是8
,對齊數(shù)是8
,i
的大小是4
,VS
默認(rèn)對齊數(shù)是8
,對齊數(shù)是4
,接下來(4>1
)整個(gè)聯(lián)合體的對齊數(shù)是4
,根據(jù)當(dāng)最?成員??不是最?對?數(shù)的整數(shù)倍的時(shí)候,就要對?到最?對?數(shù)的整數(shù)倍。此時(shí)最大成員大小是數(shù)組char [5]
大小為5
,5
不是4
的整數(shù)倍,8
(4*2
)是4
的整數(shù)倍。是不是真的是這樣呢?會(huì)不會(huì)是偶然呢?
接下來我們看第二組:
union Un2
首先short c[7]
是數(shù)組,總大小為14
,然后由于數(shù)組是按照元素的類型大小來算對齊數(shù),類型為short
類型大小為2
,VS
默認(rèn)對齊數(shù)為8
,對齊數(shù)為2
(2<8
),i
的大小是4
,VS
默認(rèn)對齊數(shù)是8
,那么對齊數(shù)是4
(4<8
),然后整個(gè)聯(lián)合體的對齊數(shù)是4
(4>2
),然后看成員最大對齊數(shù)的大?。?code>short c[7]的大小是2*7=14
)是不是整個(gè)聯(lián)合體的對齊數(shù)(4
)的整數(shù)倍,可見14
不是4
的整數(shù)倍, 根據(jù)第二條規(guī)則:當(dāng)最?成員??不是最?對?數(shù)的整數(shù)倍的時(shí)候,就要對?到最?對?數(shù)的整數(shù)倍。因此還要多用2
個(gè)字節(jié),升到16
(4*4
)個(gè)字節(jié)才是整數(shù)倍。
聯(lián)合體的對齊規(guī)則與結(jié)構(gòu)體相似:
點(diǎn)擊可以查看結(jié)構(gòu)體的內(nèi)存對齊規(guī)則——>【C語言】自定義類型:結(jié)構(gòu)體深入解析(二)結(jié)構(gòu)體內(nèi)存對齊&&宏offsetof計(jì)算偏移量&&結(jié)構(gòu)體傳參
??聯(lián)合體應(yīng)用
使?聯(lián)合體是可以節(jié)省空間的:
?如,我們要搞?個(gè)活動(dòng),要上線?個(gè)禮品兌換單,禮品兌換單中有三種商品:圖書、杯?、襯衫。
每?種商品都有:庫存量、價(jià)格、商品類型和商品類型相關(guān)的其他信息。
圖書:書名、作者、?數(shù)
杯?:設(shè)計(jì)
襯衫:設(shè)計(jì)、可選顏?、可選尺?
結(jié)構(gòu)體表示:
struct gift_list
{
//公共屬性
int stock_number;//庫存量
double price; //定價(jià)
int item_type;//商品類型
//特殊屬性
char title[20];//書名
char author[20];//作者
int num_pages;//?數(shù)
char design[30];//設(shè)計(jì)
int colors;//顏?
int sizes;//尺?
};
上述的結(jié)構(gòu)其實(shí)設(shè)計(jì)的很簡單,?起來也?便,但是結(jié)構(gòu)的設(shè)計(jì)中包含了所有禮品的各種屬性,這樣使得結(jié)構(gòu)體的??就會(huì)偏?,?較浪費(fèi)內(nèi)存。因?yàn)閷τ诙Y品兌換單中的商品來說,只有部分屬性信息是常?的。?如:商品是圖書,就不需要design
、colors
、sizes
。
所以我們就可以把公共屬性單獨(dú)寫出來,剩余屬于各種商品本?的屬性使?聯(lián)合體起來,這樣就可以介紹所需的內(nèi)存空間,?定程度上節(jié)省了內(nèi)存。
聯(lián)合體應(yīng)用:
struct gift_list
{
int stock_number;//庫存量
double price; //定價(jià)
int item_type;//商品類型
union{
struct
{
char title[20];//書名
char author[20];//作者
int num_pages;//?數(shù)
}book;
struct
{
char design[30];//設(shè)計(jì)
}mug;
struct
{
char design[30];//設(shè)計(jì)
int colors;//顏?
int sizes;//尺?
}shirt;
}item;
};
練習(xí):寫?個(gè)程序,判斷當(dāng)前機(jī)器是?端?還是?端?
- 第一種方法:
int check_sys()
{
int n = 1;//01 00 00 00 00 00 00 01
return *(char*)&n;
}
int main()
{
int ret = check_sys();
if (ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
VS運(yùn)行:
- 第二種聯(lián)合體巧妙方法:
int check_sys()
{
union
{
char c;
int i;
}u;
u.i = 1;
return u.c;//返回1是?端,返回0是?端
}
VS運(yùn)行:
小端
圖解:
大端存儲(chǔ):是指數(shù)據(jù)的低位字節(jié)內(nèi)容保存在內(nèi)存的高地址處,而數(shù)據(jù)的高位位字節(jié)內(nèi)容,保存在內(nèi)存的低地址處。
小端存儲(chǔ):是指數(shù)據(jù)的低位字節(jié)內(nèi)容保存在內(nèi)存的低地址處,而數(shù)據(jù)的高位字節(jié)內(nèi)容,保存在內(nèi)存的高地址處。
如果01
是低位字節(jié)存儲(chǔ)到低地址c
時(shí),是小端存儲(chǔ),如果01
低位字節(jié)存儲(chǔ)到高地址處,沒有存儲(chǔ)到c
的位置,那么c
的位置存儲(chǔ)著00
,返回為0
,是大端存儲(chǔ)。
??枚舉類型的聲明
枚舉類型(enum
)是一種特殊的類型,它可以為一組相關(guān)的常量值賦予用戶定義的名稱。
—>簡單來說:枚舉顧名思義就是??列舉。
枚舉類型的聲明語法:
enum 標(biāo)識符
{
枚舉常量1,
枚舉常量2,
...
} 變量;
-
enum
關(guān)鍵字聲明這是一個(gè)枚舉類型。 -
標(biāo)識符是枚舉類型的名稱。
-
在大括號{}內(nèi)列出枚舉類型的多個(gè)枚舉常量,用逗號分隔。
-
變量是枚舉類型的變量,可以直接使用枚舉類型名或枚舉常量初始化。
例如:
enum Color //Color是枚舉類型名
{
RED, // 枚舉常量
GREEN,
BLUE
} color;///color是Color類型的變量
int main()
{
printf("%d\n", RED);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
return 0;
}
輸出:
- 枚舉常量默認(rèn)從
0
開始依次累加1
。也可以手動(dòng)為枚舉常量賦值:
例如:
enum Color
{
RED = 1,
GREEN = 2,
BLUE = 4
}
運(yùn)行結(jié)果:
- 當(dāng)然第一個(gè)元素未被賦值,給其它的常量賦值,該常量前面的值是默認(rèn)值(0,1,2)后面遞增1。
例如:
enum Color
{
RED,
white,
GREEN = 8,
BLUE ,
BLACK,
};
int main()
{
printf("%d\n", RED);
printf("%d\n", white);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
printf("%d\n", BLACK);
return 0;
}
輸出:
??枚舉類型的優(yōu)點(diǎn)
為什么使?枚舉?
我們可以使? #define 定義常量,為什么?要使?枚舉?
枚舉的優(yōu)點(diǎn):
- 增加代碼的可讀性和可維護(hù)性
如:之前的掃雷中可以這樣定義用PLAY
代替1
,EXIT
代替0
,更具有個(gè)性化:
#include <stdio.h>
#include <string.h>
// 定義游戲選擇枚舉類型
enum Game_Selection
{
EXIT, // 退出游戲
PLAY // 開始游戲
};
// 打印菜單
void menu()
{
printf("********** Menu **********\n");
printf("PLAY - Start the game\n");
printf("EXIT - Exit the game\n");
printf("********** Menu **********\n");
}
int main()
{
enum Game_Selection input; // 聲明游戲選擇變量
char choice[10]; // 聲明選擇輸入緩沖區(qū)
do
{
menu(); // 調(diào)用菜單函數(shù)
printf("Please enter your choice: ");
scanf("%s", choice); // 讀取選擇輸入
getchar(); // 清除輸入緩沖區(qū)
if (strcmp(choice, "PLAY") == 0)
{
input = PLAY; // 設(shè)置選擇為開始游戲
}
else if (strcmp(choice, "EXIT") == 0)
{
input = EXIT; // 設(shè)置選擇為退出游戲
}
else
{
printf("輸入錯(cuò)誤,請重新輸入\n");
continue; // 輸入錯(cuò)誤,繼續(xù)循環(huán)
}
switch (input)
{
case PLAY:
printf("掃雷游戲啟動(dòng)!\n");
break;
case EXIT:
printf("不玩了,啟動(dòng)不了!\n");
break;
}
} while (input != EXIT);
return 0;
}
代碼運(yùn)行:
-
和
#define
定義的標(biāo)識符?較枚舉有類型檢查,更加嚴(yán)謹(jǐn)。 -
便于調(diào)試,預(yù)處理階段會(huì)刪除
#define
定義的符號 -
使??便,?次可以定義多個(gè)常量
-
枚舉常量是遵循作?域規(guī)則的,枚舉聲明在函數(shù)內(nèi),只能在函數(shù)內(nèi)使?
?? 枚舉類型的使?
那是否可以拿整數(shù)給枚舉變量賦值呢?在C語?中是可以的,但是在C++是不?的,C++的類型檢查?較嚴(yán)格。
-
在C語言中,枚舉類型實(shí)際上就是整數(shù)類型,編譯器會(huì)把枚舉常量替換成對應(yīng)的整數(shù)值。所以可以用整數(shù)直接給枚舉變量賦值。
-
而在C++中,枚舉類型是完全獨(dú)立的類型。編譯器會(huì)檢查類型是否匹配,不允許用整數(shù)直接給枚舉變量賦值。
例如:
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//C語言
enum Color//顏?
{
RED = 1,
GREEN = 2,
BLUE = 4
};
int main()
{
enum Color c;
c = 1; // 可以直接賦值整數(shù)
return 0;
}
輸出:
// C++語言
enum Color//顏?
{
RED = 1,
GREEN = 2,
BLUE = 4
};
Color c;
c = 1; // 錯(cuò)誤,類型不匹配
輸出:
總結(jié):
C語言中枚舉類型實(shí)際上就是整數(shù),允許用整數(shù)直接賦值
C++中枚舉類型是獨(dú)立類型,不允許用整數(shù)直接賦值,需要強(qiáng)制類型轉(zhuǎn)換
??總結(jié)
這次阿森和你一起學(xué)習(xí)聯(lián)合體類型的聲明,特點(diǎn),然后進(jìn)行相同成員的結(jié)構(gòu)體和聯(lián)合體對?,??的計(jì)算,聯(lián)合體應(yīng)用,枚舉類型的聲明,優(yōu)點(diǎn)和掃雷改造使?方法,阿森將下一節(jié)和你一起學(xué)習(xí)動(dòng)態(tài)內(nèi)存管理?? 。文章來源:http://www.zghlxwxcb.cn/news/detail-768279.html
感謝你的收看,如果文章有錯(cuò)誤,可以指出,我不勝感激,讓我們一起學(xué)習(xí)交流,如果文章可以給你一個(gè)小小幫助,可以給博主點(diǎn)一個(gè)小小的贊??文章來源地址http://www.zghlxwxcb.cn/news/detail-768279.html
到了這里,關(guān)于【C語言】一篇文章深入解析聯(lián)合體和枚舉且和結(jié)構(gòu)體的區(qū)別的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!