上一節討論了C語言的 union 語法的一個使用場景,讀者應該發現,union 成員共享一塊內存的特性使得C語言程式設計師能夠寫出更加節約資源的程序。不過也有讀者在看了上一節文章後,評論或者私信說了一些關於C語言 union 的特性,這些回復有些是不嚴謹的。
如果不能完全掌握 union 語法,在使用時難免會覺得困惑,甚至跳入「陷阱」,寫出有隱患的C語言代碼。我稍稍總結了一下讀者回覆中關於 union 語法的誤解,主要可以分為兩大類。
誤區1:union 成員必須等長度
下面這段C語言代碼節選自上一節:
有讀者評論稱:「要是union不同成員的大小不一樣就不能這麼用了」。他認為C語言的 union 成員必須是等長的,否則就不可使用。這麼認為也有一定的理由:union 成員共用一塊內存,如果成員不等長,那差異長度怎麼算呢?
那麼,C語言中的 union 成員到底是不是必須長度相等呢?下面是一個例子,請看:
顯然,成員 c 和 i 長度並不相等,那是否這麼定義 u 就不對了呢?我們編寫相應的C語言代碼測試之:
編譯這段C語言代碼並執行,得到如下輸出:
可見編譯器並沒有報錯。雖然我們僅使用了 union 的成員 c(佔用1位元組),但是 test 仍然佔用了 4 個字節。顯然,即使 union 的成員不等長,使用起來也完全沒有問題,而且 union 佔用內存的空間等於最長的那個成員佔用的內存空間。
誤區2:使用 union 必須考慮大小端問題
有讀者(@Fishmoo)回覆說:「共用體(union)要在確定硬體大小端的情況下使用」。這個觀點對不對呢?在回答這個問題之前,首先需要了解「大小端」的概念。
簡單來說,「大小端」是指數據在內存中的字節順序。例如 int 型變量 a=0x12345678 佔用 4 個字節,在大端字節序的機器上,int 數據的高位位於低地址。在小端字節序的機器上,int 數據的高位位於高地址。
關於「大小端」這個名稱,有一段比較有趣的故事:
Lilliput和Blefuscu這兩個強國在過去的36個月中一直在苦戰。戰爭的原因:大家都知道,吃雞蛋的時候,原始的方法是打破雞蛋較大的一端,可以那時的皇帝的祖父由於小時侯吃雞蛋,按這種方法把手指弄破了,因此他的父親,就下令,命令所有的子民吃雞蛋的時候,必須先打破雞蛋較小的一端,違令者重罰。然後老百姓對此法令極為反感,期間發生了多次叛亂,其中一個皇帝因此送命,另一個丟了王位,產生叛亂的原因就是另一個國家Blefuscu的國王大臣煽動起來的,叛亂平息後,就逃到這個帝國避難。據估計,先後幾次有11000餘人情願死也不肯去打破雞蛋較小的端吃雞蛋。這個其實諷刺當時英國和法國之間持續的衝突。Danny Cohen一位網絡協議的開創者,第一次使用這兩個術語指代字節順序,後來就被大家廣泛接受。
那麼,大小端和 union 有什麼關係呢?讀者應該明白,C語言中 union 語法最重要的性質就是成員共享一塊內存區域,我們改寫上面的C語言代碼:
因為在我的機器上,成員 i 佔用 4 個字節內存空間,所以將成員 c 改為數組 char c[4]。現在編寫下面這段C語言代碼:
這段C語言代碼會輸出什麼呢?因為成員 c 和 成員 i 共享同一塊內存,所以 c 在內存中的起始地址和 i 的一致,i 是一個 4 字節變量,它的值在內存中分布因機器「大端」和「小端」而異。編譯這段C語言代碼並執行,得到如下輸出:
# gcc t.c# ./a.out0x780x560x340x12
可見,高位在低地址,此時C語言程序運行在「大端」機器上。讀者可自行測試上面這段C語言代碼,應該會發現,如果自己的機器是「小端」架構,則C語言程序最終的輸出會與這裡有一定的差異。
現在知道「大小端」與C語言 union 的關係了。那是不是只要使用 union,就一定要考慮「大小端」問題呢?當然不是了,例如上一節中的 union 例子,就完全不需要考慮「大小端」問題。只有涉及到使用「小變量」解釋「大變量」內存區域時,才需要考慮「大小端」問題。
看了本專欄之前文章的讀者應該明白,C語言中的數據類型的一大作用就是告訴編譯器該如何解釋它所在內存裡存放的數據的。例如一個 4 字節內存區域:
編譯器要將這塊內存裡的數據取出使用,先要知道該怎麼取——它這塊內存可以表示 4 個 char 型變量,也可以表示 2 個 short 型變量,還可以表示 1 個 int 型變量。如果告訴編譯器這段內存表示一個 int 型數據,編譯器會自動按照當前機器的「大小端」解釋這個數據,就無需程式設計師再多操心了。如果告訴編譯這段內存表示 4 個 char 型變量(小變量),則編譯器會按照地址順序解釋這段內存,輸出就與「大小端」有關了。
小結
本節簡要介紹了C語言 union 語法的性質,較為詳細的討論了上一節讀者關於 union 的兩個誤區。現在應該明白,C語言的 union 成員不一定是等長的,使用 union 並不總是需要考慮「大小端」問題。事實上,「大小端」問題不是 union 語法的專屬考慮,在C語言程序開發中,很多情況都需要考慮這個問題,以後有機會再說了。
歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。