大小端轉換定義結構體的技巧

2021-08-10 05:11:21 字數 2600 閱讀 7255

首先,不管是哪一種資料存放方式,對於乙個單位元組的資料,儲存方式都是一樣的。比如uint8_t類,char類在大端和小端都可以不經過轉換直接讀取。

而對於兩位元組及以上位元組大小的資料,大小端的儲存方式就有區別了。大端(網路序)的儲存方式與我們的閱讀邏輯是相同的。大端的儲存方式是高位儲存在低位址中,小端(常見的pc機)的儲存方式則是高位存高位址。

舉個例子就明白了,比如有個數字0x12345678,儲存到記憶體至少需要4位元組,按照大端的儲存方式,則是0x12 0x34 0x56 0x78,與我們的閱讀邏輯一致;而小端的儲存方式則是0x78 0x56 0x34 0x12。記住大端儲存方式(網路序)與我們的閱讀邏輯相同就可以了。

那麼根據上面的分析,我們就會發現,大小端基於記憶體的顛倒是以位元組為單位的,不是以每一位為單位的,這一點在具體的**實現中尤其重要。

假定我們有乙個uint16_t型別的資料,在小端機器上面處理(現在的pc機基本都是小端機)需要轉換成大端怎麼轉換呢?前一位元組與後一位元組位置換。

假定我們有乙個uint32_t型別的資料,在小端機器上面處理(現在的pc機基本都是小端機)需要轉換成大端怎麼轉換呢?前一位元組與後一位元組置換,中間兩個位元組也互相置換。

那麼,再進一步,如果有乙個結構體,大小32位(4位元組),定義如下:

struct test_u32{

uint8_t a;

uint8_t b;

uint8_t c;

uint8_t d;

在大小端轉換時,自然是a和d互換,b和c互換。因此,完全可以在小端定義結構體時,就定義成如下結構:

struct test_u32{

uint8_t d;

uint8_t c;

uint8_t b;

uint8_t a;

這樣在進行網路通訊時就能省去了大小端轉換這個步驟,在大小端轉換較多的情況下,通過改變結構體定義的方式盡可能省去大小端轉換的步驟,對於資料分析和網路傳輸的速率提公升是明顯的。

那麼接下來再考慮一下情況,如果上述結構體在大端機定義如下:

struct test_u32_2{

uint8_t a;

uint16_t b;

uint8_t c;

如果只是在小端機定義成如下形式,會有什麼影響嗎?

struct test_u32_2{

uint8_t c;

uint16_t b;

uint8_t a;

明顯的,a和c由於他們的定義不大於1位元組,因此a和c的資料不會有錯。而由於b大於1位元組了,所以仍然需要進行大小端轉換。這裡就是乙個陷阱。

另外在實際的網路私有協議定義中,不可能所有的資料都是基於8位(1位元組)對齊,有些資料可能只佔1位,有些資料可能佔35位,遇到這種情況該如何處理呢?

首先,如果是如下情況,n個變數瓜分乙個位元組:

struct test_u8{

uint8_t a:1;

uint8_t b:3;

uint8_t c:4;

此時變數a佔1位,變數b佔3位,變數c佔4位,他們共享乙個位元組。乙個位元組是8位,在記憶體中的儲存方式是「abbbcccc」(每個字元代表該位儲存著哪個變數的資料)

對於這種情況,可以直接在小端定義中進行置換:

struct test_u8{

uint8_t  c:4;

uint8_t b:3;

uint8_t a:1;

在小端如此定義,記憶體中的資料儲存方式依然是abbbcccc。

那麼再討論乙個問題,如果在網路序機器中定義如下結構體,小端中也可以如上一例子直接置換嗎?

struct test_u16{

uint16_t a:3;

uint16_t b:1;

uint16_t c:12;

這個時候我會想當然的認為可以置換成如下形式,但是列印結果告訴我出錯了。

struct test_u16{

uint16_t c:12;

uint16_t b:1;

uint16_t a:3;

比如我在置換後的結構體中,令a=1,然後按照uint16_t型別輸出該結構體資料,按照預想情況應該是0x2000,但是實際列印資料是0x0100。根據列印結果推原因,出錯理由如下。

1.在大端中,資料儲存方式應當如下:aaabcccc cccccccc。那麼如果a=1,b=0,c=0,資料當是0010000 00000000,也就是0x2000。但是現在儲存內容成了00000001 00000000,變成了0x0100,那麼此時在記憶體中,資料儲存形式為***xxaaa ******xx (x表示未知是b還是c的儲存位),經過測試,如此錯誤定義結構體,使得本來的資料儲存方式變為了ccccbaaa cccccccc 。為什麼?這個需要自己畫圖想一下,注意大小端的轉換是以位元組為單位轉換而不是以位為單位轉換的。

因此需要修改定義為:

struct test_u16{

uint8_t c_1:4;

uint8_t b:1;

uint8_t a:3;

uint8_t c_2;

這時令a=1,b=0,c_1=0,c_2=0,結構體安裝uint16_t輸出便是0x2000。

結論:在大小端處理時遇到不是整自己的變數,定義結構體以uint8_t為單位定義,對於超過一位元組又不足兩位元組的變數,要拆成兩部分處理。

結構體的大小

對齊 現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定變數的時候經常在特定的記憶體位址訪問,這就需要各型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。對齊的作用和原因 各個硬體平台對儲存空間的處...

結構體的大小

typedef 定義型別 typedef unsigned long long uint64 定義型別,起外號 例如 typedef struct student student 注意 typedef int pfun int,int 函式指標 指向函式的指標 typedef int pint de...

結構體的大小

結構體中的成員可以是不同的資料型別,成員按照定義時的順序依次儲存在連續的記憶體空間。和陣列不一樣的是,結構體的大小不是所有成員大小簡單的相加,需要考慮到系統在儲存結構體變數時的位址對齊問題。看下面這樣的乙個結構體 struct stu1 先介紹乙個相關的概念 偏移量。偏移量指的是結構體變數中成員的位...