C語言union用於打包和拆包資料

2021-10-10 06:18:19 字數 3864 閱讀 7243

使用union來打包/拆包資料union ;

} u1;

複製**

此union內部有兩個成員:第乙個成員「 word」是乙個兩位元組的變數。第二個成員是兩個單位元組變數的結構。為聯合分配的兩個位元組在其兩個成員之間共享。

分配的記憶體空間可以如下圖1所示。

圖1

「 word」變數是指整個分配的記憶體空間,「 byte1」和「 byte2」變數是指構成「 word」變數的一位元組區域。我們如何使用此功能?假設您有兩個單位元組變數「 x」和「 y」,應將其組合以產生單個兩位元組變數。

在這種情況下,您可以使用上述聯合並為結構成員分配「 x」和「 y」,如下所示:

u1.byte1 = y;

u1.byte2 = x;

複製**

現在,我們可以讀取並集的「 word」成員,以獲得由「 x」和「 y」變數組成的兩位元組變數(參見圖2)。

圖2

上面的示例顯示了使用並集將兩個乙個位元組的變數打包為單個兩個位元組的變數。我們也可以做相反的事情:將兩個位元組的值寫入「 word」,然後通過讀取「 x」和「 y」變數將其解壓縮為兩個乙個位元組的變數。將值寫入工會的乙個成員並讀取工會的另乙個成員有時被稱為「資料修剪」。

處理器位元組序

當使用聯合對資料進行打包時,我們需要注意處理器的位元組順序。正如羅伯特·基姆(robert keim)關於位元組序的文章所討論的那樣,該術語指定了資料物件的位元組在記憶體中儲存的順序。處理器可以是小端或大端。使用big-endian處理器時,資料的儲存方式是,包含最高有效位的位元組具有最低的記憶體位址。在小端系統中,包含最低有效位的位元組首先儲存。

圖3中所示的示例說明了序列0x01020304的小端和大端儲存。

圖3.由iar提供。

#include

#include

int main()

;uint16_t    word;

} u1;

u1.byte1 = 0x21;

u1.byte2 = 0x43;

printf("word is: %#x", u1.word);

return 0;

}複製**

執行此**,我得到以下輸出:

詞是:0x4321

這表明共享儲存空間的第乙個位元組(「 u1.byte1」)用於儲存「 word」變數的最低有效位元組(0x21)。換句話說,我用來執行**的處理器是little endian。

如您所見,聯合的特定應用程式可能表現出與實現有關的行為。但是,這不是乙個嚴重的問題,因為對於這樣的低階編碼,我們通常知道處理器的位元組序。如果我們不知道這些細節,我們可以使用上面的**來查詢資料在記憶體中的組織方式。

替代解決方案

除了使用並集,我們還可以使用按位運算子來執行資料打包或拆包。例如,我們可以使用以下**來組合兩個乙個位元組的變數「 byte3」和「 byte4」,並產生乙個單個的兩個位元組的變數(「 word2」):

word2 = (((uint16_t)   byte3) << 8 ) | ((uint16_t)   byte4);

複製**

讓我們比較一下小端和大端兩種情況下這兩種解決方案的輸出。考慮下面的**:

#include

#include

int main()

;uint16_t    word1;

} u1;

u1.byte1 = 0x21;

u1.byte2 = 0x43;

printf("word1 is: %#x\n", u1.word1);

uint8_t       byte3, byte4;

uint16_t      word2;

byte3 = 0x21;

byte4 = 0x43;

word2 = (((uint16_t) byte3) << 8 ) | ((uint16_t) byte4);

printf("word2 is: %#x \n", word2);

return 0;

}複製**

如果我們針對大型位元組序處理器(例如tms470mf03107)編譯此**,則輸出為:

word1是:0x2143

word2是:0x2143

但是,如果我們針對像stm32f407ie這樣的小端序處理器對其進行編譯,則輸出將是:

word1是:0x4321

word2是:0x2143

儘管基於聯合的方法表現出與硬體有關的行為,但是基於移位操作的方法卻得到相同的結果,而不管處理器的位元組順序如何。這是由於以下事實:在後一種方法中,我們為變數的名稱(「 word2」)分配了乙個值,並且編譯器負責該裝置使用的記憶體組織。但是,使用基於聯合的方法,我們正在更改構成「 word1」變數的位元組的值。

儘管基於聯合的方法表現出與硬體有關的行為,但它具有更易讀和可維護的優點。這就是為什麼許多程式設計師更喜歡在該應用程式中使用聯合的原因。

「資料校正」的實際示例

在使用常見的序列通訊協議時,我們可能需要執行資料打包或拆包。考慮乙個序列通訊協議,該協議在每個通訊序列期間傳送/接收乙個位元組的資料。只要我們使用一位元組長的變數,就很容易傳輸資料,但是如果我們有乙個任意大小的結構應該通過通訊鏈結怎麼辦?在這種情況下,我們必須以某種方式將資料物件表示為一位元組長的變數陣列。一旦獲得了位元組陣列表示,就可以通過通訊鏈結傳輸位元組。然後,在接收器端,我們可以適當地打包它們並重建原始結構。

例如,假設我們需要通過uart通訊傳送乙個浮點變數「 f1」。浮點變數通常占用四個位元組。因此,我們可以將以下並集用作提取「 f1」的四個位元組的緩衝區:

union ;

} u1;

複製**

#include

#include

int main();};

union buffer buff_tx;

union buffer buff_rx;

buff_tx.f = f1;

buff_rx.byte[0] = buff_tx.byte[0];

buff_rx.byte[1] = buff_tx.byte[1];

buff_rx.byte[2] = buff_tx.byte[2];

buff_rx.byte[3] = buff_tx.byte[3];

printf("the received data is: %f", buff_rx.f);

return 0;

}複製**

下面的圖4展示了所討論的技術。請注意,位元組是順序傳輸的。

圖4

結論

聯合的原始應用程式建立了互斥變數的共享儲存區,但隨著時間的流逝,程式設計師已經廣泛使用聯合用於完全不同的應用程式:使用聯合進行資料打包/拆包。工會的這種特殊應用涉及將值寫入工會的乙個成員並讀取工會的另乙個成員。

「資料修剪」或使用聯合進行資料打包/拆包可能導致依賴於硬體的行為。但是,它具有更具可讀性和可維護性的優點。這就是為什麼許多程式設計師更喜歡在該應用程式中使用聯合的原因。當我們有任意大小的資料物件應通過序列通訊鏈結時,「資料修剪」將特別有用。

C語言union用於打包和拆包資料

union u1 複製 此union內部有兩個成員 第乙個成員 word 是乙個兩位元組的變數。第二個成員是兩個單位元組變數的結構。為聯合分配的兩個位元組在其兩個成員之間共享。分配的記憶體空間可以如下圖1所示。圖1 word 變數是指整個分配的記憶體空間,byte1 和 byte2 變數是指構成 w...

C語言之struct和union分析

1.struct的小秘密 c語言中的struct可以看做變數的集合 struct的問題 空結構體占用多大記憶體?相關測試 include struct ts int main 實驗結果 以上結果是執行在gcc編譯器,如果使用bcc編譯器,則結果不同,此問題屬於c語言中灰色地帶 2.結構體與柔性陣列 ...

c語言的union和大小端模式

在c語言中允許不同型別的資料使用同一段記憶體,也就是不容型別的變數存放起始位址相同的記憶體中,雖然他們占用的位元組數可能不同,但是起始位址相同。共用體就是這樣的型別,它採用的是覆蓋儲存技術,允許不同型別資料互相覆蓋,共享同一段記憶體。如下 include union x void main 雖然,沒...