C語言學習篇(23)——共用體union

引言

在C語言中,每個數據類型都有其特定的作用或使用場景。 前面我們詳細的分析了數組,結構體,今天我們來講講共用體(union),所謂的共用體就是使用覆蓋技術讓數據成員之間相互覆蓋,以實現不同數據成員共用同一段內存。先引出概念,接下來我們從共用體與其他的數據類型區別,到共用體類型如何定義,哪些場景會使用共用體等等,逐步分析。

共用體和結構體區別

共用體(union)和結構體(struct)在類型定義, 變量定義,使用方法上都非常相似:

<code>//共用體類型定義
union test
{
\tint a
\tchar b;
};/<code>

變量的定義和使用:

<code>//定義共用體變量t1
\tunion test t1;
\t
//使用共同體t1成員
\tt1.a = 23;
\t/<code>

那兩者有什麼區別呢?結構體好比是一個袋子,袋子裡面裝了各種各樣的東西,每個東西都是獨立存在的,佔據了袋子體積; 而共用體的各個成員是一體的,彼此不獨立,它們使用同一塊內存空間,可以理解為:有時候是這個元素,有時候是另外一個元素(就像是一個多面人),有不同的形態,更準確的說是同一塊內存空間有多種解釋方式!通過以下示意圖感受下:

C語言學習篇(23)——共用體union

接下來我們通過實例代碼,來理解其本質區別:

<code>#include <stdio.h>

//定義結構體類型
struct test1
{
\tint a;
\tchar b;
};

//定義共用體類型
union test2
{
\tint a;
\tchar b;
};

int main(void)
{
//定義結構體變量t1
\tstruct test1 t1;
//定義共用體變量2
\tunion test2 t2;
\t
\tt1.a = 23;
\tt2.a = 24;
\t
//打印變量元素
\tprintf("t1.b = %d.\\n", t1.b);
\tprintf("t2.b = %d.\\n", t2.b);

//打印結構體t1元素的地址
printf("&t1.a = %p.\\n", &t1.a);
\tprintf("&t1.b = %p.\\n", &t1.b);
\t
//打印共用體t2元素地址
\tprintf("&t2.a = %p.\\n", &t2.a);
\tprintf("&t2.b = %p.\\n", &t2.b);
\t
\treturn 0;

}/<stdio.h>/<code>

說明:

1.我們分別定義了結構體變量t1, 和共用體變量t2

2. 初始化了結構體t1中的元素a, 賦值為23,但是元素b未初始化

3. 初始化了共用體t2中的元素a, 賦值為24,同樣元素b未初始化

4. 之後我們分別打印了t1和t2成員的地址


接下來我們編譯運行,看下結果:

C語言學習篇(23)——共用體union

可以看到結構體t1的元素b未初始化,是一個隨機值(因為我們定義的t1是局部變量,定義在棧上,如果不清楚什麼是棧,什麼是堆,以及它們的特性,可以查閱我之前的文章~)。

而共用體t2元素b未初始化, 但是它的值與元素a居然相同(這不是湊巧哦,不相信的小夥伴可以自己測試下,當然前提是a的初始化的值不能超過元素b的類型值域, 同時這裡會涉及大小端的問題,這個之後會詳細分析)。同時可以看到結構體t1的元素a和b的地址是不同的, 而共用體t2的元素a和b地址是完全一致!相信到這裡大家能基本瞭解了共用體的特性: 成員之間共用一塊內存空間,只是解析方式不同。

共用體的大小

經過上面的講述,大家知道了共用體的成員其實是共享了一塊內存空間,哪麼當我們頂一個共用體類型時,它實際佔用的內存空間是多少呢?

我們先舉一個簡單的例子:

<code>#include <stdio.h>

union test
{

\tint a;
\tchar b;\t
};

int main(void)
{
\tunion test t1;
\t
//打印共用體成員的數據類型大小
\tprintf("sizeof(int) = %d.\\n", sizeof(int));
\tprintf("sizeof(char) = %d.\\n", sizeof(char));
\t
//打印共用體大小
\tprintf("sizeof(union test) = %d.\\n", sizeof(union test));
\t
\treturn 0;
}/<stdio.h>/<code>

編譯運行結果:

C語言學習篇(23)——共用體union

可以看到共用體變量t1的內存大小為4字節, 即原則上,共用體大小取決於佔據最多內存的成員的長度。需要注意這裡用的“原則”一詞,因為有時候需要考慮內存對齊問題。 我們通過以下例子來加深理解:

<code>#include <stdio.h>

//通過__attribute__指令設置1字節對齊
__attribute__((aligned(1)))
struct mystruct
{
\tchar a;
\tint b;
\tint c;
\tint d;
\tint e;
\tdouble f;
\tchar g;
\tshort h;
\tdouble i;
\tfloat j;
\t
}__attribute__((packed));

//通過__attribute__指令設置1字節對齊
__attribute__((aligned(1)))
union test
{
\tint a;
\tchar b;
\tfloat c;
\tdouble d;
\t
\tstruct mystruct my1;
}__attribute__((packed));

int main(void)
{//定義了共用體變量t1
\tunion test t1;

\t
//打印了結構體大小
\tprintf("sizeof(struct mystruct) = %d.\\n", sizeof(struct mystruct));
//打印共用體大小
\tprintf("sizeof(union test) = %d.\\n", sizeof(union test));
\t
\treturn 0;
}/<stdio.h>/<code>

分析:

1. 首先我們定義了結構體類型mystruct, 並使用__attribute__指令設置為1字節對齊

2. 定義了共用體類型,其中一個成員是上面定義的結構體mystruct類型, 同時也使用__attribute__指令設置為1字節對齊

3. 定義了共用體變量t1

4. 分別打印了結構體mystruct類型大小,和共用體test 類型大小

OK, 編譯運行:

C語言學習篇(23)——共用體union

我們可以看到共用體的大小就等於成員my1(結構體類型)的大小,再次驗證了上面的結論。


共用體的訪問方式

上面我們舉得的例子都是使用下標式訪問共用體成員,還是和數組,結構體一樣, 下標式訪問知識編譯器提供的一種語法糖, 方便開發人員使用和理解, 但是我們需要了解其本質: 共享一塊內存,只是解析方式不同。 因此我們可以通過指針方式來訪問,這也是真正理解共用體的判斷標準。

<code>#include <stdio.h>

union test
{
\tint a;
\tfloat b;
};

int main(void)
{
\tunion test t1;
\t
//下標式訪問
\tt1.a = 1123477094;
\tprintf("t1.b = %f.\\n", t1.b);
\t
//指針式訪問
\tint a = 1123477094;
\tprintf("指針方式: t1.b = %f.\\n", *(float *)&a);
\t
\treturn 0;

}/<stdio.h>/<code>

編譯運行:

C語言學習篇(23)——共用體union

從結果再次驗證共用體實質:共享一塊內存,只是解析方式不同。


總結

通過以上分析,我們瞭解了共用體的實質,如何定義和使用,以及與結構體的區別等。 之後我會講解共用體的應用場景,尤其是如何使用共用體測試系統大小端,這也是面試和筆試中出現的概率非常高的題目,如果你對大小端的概念不是很清楚,或者想了解有哪些辦法測試系統的大小端,歡迎轉發,收藏,關注,後續內容更加精彩喲~


C語言學習篇(23)——共用體union


分享到:


相關文章: