天天看點

聯合體union用在何處?

  程式設計初學者在學習時,總想問:“這個東東有什麼用?”于是,在建設有關的教學資源時,也便總從這個角度,試圖給出一些案例,這是一個将初學者作為教學目标人群的人該幹的事。

  然而,在準備這樣一些案例時,諸如循環、數組、結構體之類的,可以編出一堆堆的能展現實際應用的案例,或出例題,或出實踐題目,都好說。然而,對于有些在教科書中的“小知識點”,作為講程式設計語言,有些老師都選擇不講的,能卻總是不易編出。

  主要的原因,是這些内容要解決的問題,似乎并不常見。針對特殊的應用場合,不知道初學者是否能耐着性子将應用背景搞明白。而不少能真正應用的場合,還涉及到一些基礎的知識。

  對于我的工作,繼續花時間建設讓初學者在有限積累前提下就能“提前體驗”的方法和資源,而也寄語初學者,不少有大用的知識,限于學習者學習的階段性,繼續前行你才能知道。在這個階段,别人想給你講清,并不現實。人說這是個浮躁的時代,靜心讀書,不惜尚不知其用也要學下去,這可以是一種選擇。

  

  編寫一段程式判斷系統中的cpu 是little endian 還是big endian 模式?

分析:

  作為一個計算機相關專業的人,我們應該在計算機組成中都學習過什麼叫little endian 和big endian。little endian 和big endian 是cpu 存放資料的兩種不同順序。對于整型、長整型等資料類型,big endian 認為第一個位元組是最高位位元組(按照從低位址到高位址的順序存放資料的高位位元組到低位位元組);而little endian 則相反,它認為第一個位元組是最低位位元組(按照從低位址到高位址的順序存放資料的低位位元組到高位位元組)。

  例如,假設從記憶體位址0x0000 開始有以下資料:

0x12 0x34 0xab 0xcd

  如果我們去讀取一個位址為0x0000 的四個位元組變量,若位元組序為big-endian,則讀出結果為0x1234abcd;若位元組序位little-endian,則讀出結果為0xcdab3412。如果我們将0x1234abcd 寫入到以0x0000 開始的記憶體中,則little endian 和big endian 模式的存放結果如下:

位址 0x0000 0x0001 0x0002 0x0003 big-endian 0x12 0x34 0xab 0xcd little-endian 0xcd 0xab 0x34 0x12

  一般來說,x86 系列cpu 都是little-endian 的位元組序,powerpc 通常是big endian,還有的cpu 能通過跳線來設定cpu 工作于little endian 還是big endian 模式。

解答:

  顯然,解答這個問題的方法隻能是将一個位元組(char/byte 類型)的資料和一個整型資料存放于同樣的記憶體始位址,通過讀取整型資料,分析char/byte 資料在整型資料的高位還是低位來判斷cpu 工作于little endian 還是big endian 模式。

  得出如下的答案:

  除了上述方法(通過指針類型強制轉換并對整型資料首位元組指派,判斷該指派賦給了高位還是低位)外,還有沒有更好的辦法呢?我們知道,union 的成員本身就被存放在相同的記憶體空間(共享記憶體,正是union 發揮作用、做貢獻的去處),是以,我們可以将一個char/byte 資料和一個整型資料同時作為一個union 的成員,得出如下答案:

  實作同樣的功能,我們來看看linux 作業系統中相關的源代碼是怎麼做的:

  linux 的核心作者們僅僅用一個union 變量和一個簡單的宏定義就實作了一大段代碼同樣的功能!由以上一段代碼我們可以深刻領會到linux 源代碼的精妙之處!(如果endianness=’l’表示系統為little endian,為’b’表示big endian )

  假設網絡節點a 和網絡節點b 中的通信協定涉及四類封包,封包格式為“封包類型字段+封包内容的結構體”,四個封包内容的結構體類型分别為structtype1~ structtype4,請編寫程式以最簡單的方式組織一個統一的封包資料結構。

  封包的格式為“封包類型+封包内容的結構體”,在真實的通信中,每次隻能發四類封包中的一種,我們可以将四類封包的結構體組織為一個union(共享一段記憶體,但每次有效的隻是一種),然後和封包類型字段統一組織成一個封包資料結構。

  根據上述分析,我們很自然地得出如下答案:

繼續閱讀