程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 數組到結構體轉換和聯合體與位段結合中的大小端問題

數組到結構體轉換和聯合體與位段結合中的大小端問題

日期:2017/1/16 15:17:56      編輯:關於PHP編程

數組到結構體轉換和聯合體與位段結合中的大小端問題


移植驅動代碼時發現工程中使用了如下形式的代碼

typedef struct BlkTestTag{

uint16_tm_u1Var1;

uint8_t reserve[2];

uint32_tm_u4Var2;

}BlkTest;

uint8_t test_array[8] ={0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

pblkTest = (BlkTest *)test_array;

u2Var1 = pblkTest->m_u1Var1;

u4Var2 = pblkTest->m_u4Var2;

本意是為了將結構體的值以數組常量形式給出。

讓var1 = 0x0201; var2 =0x08070605

再將這些代碼移植到另一款MCU時發現工作不正常。之後發現移植的這款MCU是大端的,而我們之前的使用的MCU都是小端的。於是定位上上面的數組賦值代碼。

在小端MCU下會按我們想要的結果來賦值,如下圖。Var1和var2的結果是我們想要的。


但是如果再大端MCU上執行的話結果就大不一樣。




原因在於數組uint8_t test_array[8] = {0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

其數據在內存中是從低地址到高地址存放的,如下圖



即0x20000000地址處為0x01, 0x20000000地址處為0x02……

所以直接將數組名轉換成結構體指針後,在訪問m_u1Var1成員時,該成員為2字節無符號整型,所以他會將0x20000000 和0x20000001 地址處的兩個值當做 m_u1Var1

所以如果是小端MCU 低地址0x20000000的值0x01會被解釋為低字節

高地址0x20000001的值0x02會被解釋為高字節

於是值就是 0x0201

如果是大端MCU 低地址0x20000000的值0x01會被解釋為高字節

高地址0x20000001的值0x02會被解釋為低字節

於是值就是 0x0102

這個數組到結構體的轉換問題,在知道了大小端後問題定位還比較容易。

另一個關於聯合體的問題就有點偏,即字節內的bit序在大小端MCU上也是不同的。

工程中存在如下代碼

typedef union UnTestTag{

uint8_tvar;

struct{

uint8_tbit0:1;

uint8_tbit1:1;

uint8_tbit2:1;

uint8_tbit3:1;

uint8_tbit4:1;

uint8_tbit5:1;

uint8_tbit6:1;

uint8_tbit7:1;

}pixels;

}UnTest;

在對聯合體賦值後

unTest.var = 0x11; 二進制值為 00010001

上面的聯合體定義就是為了判斷某些位,所以我們希望的是賦值0x11後,位段的unTest.pixels.bit0和unTest.pixels.bit4的值應該為1 才對。
即我們希望的是名字能反應真正的bit值

在小端MCU中運行時的確是正確的。



但是在大端MCU中運行時結果如下圖。

即聯合體中 unTest.pixels.bit0的值反應的不再是真正的二進制中的bit0了

反而是unTest.pixels.bit7 反應的是真正的bit0.

即unTest.pixels.bit0- unTest.pixels.bit7 反應的真實值其實是bit7-bit0了





一般情況下 字節內的bit序 是不可見的,除非使用了上面的位段的情況下才會發現。
既然字節內bit序與大小端有段,那麼為什麼使用在使用 <<, >>之類操作符時。都可以正常運行,這是芯片根據會自己更具自己大小端類型自動做相應的處理。

具體如下:
對於小端 如果 bit0 - bit7為 1000 1000
小端MCU認為這個值為 0x11 當做 << 操作後 二進制為0100 0100 小端MCU認為其值為0x22 ,的確是0x11的左移操作結果
可以看出這裡的 << 實際操作是向 bit7方向移位了

對於大端 如果bit0-bit7為 1000 1000
大端MCU認為這個值為0x88 當做 << 操作後 二進制為 0001 0000 ,大端MCU認為其值為0x10, 也的確是 0x88左移溢出後的結果

但是這裡的 << 實際操作是向bit0方向移位了


附上一段簡單測試代碼,在keil工程中 選擇模擬器調試



然後勾選big endian的情況下 debug實例代碼,然後打端點查看變量值,結果就是大端情形下的。不勾選 big endian 再debug跑一次就是小端情形下的



typedef struct BlkTestTag{
uint16_t m_u1Var1;
uint8_t reserve[2];
uint32_t m_u4Var2;
}BlkTest;

uint8_t test_array[8] = {0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

typedef union UnTestTag{
uint8_t var;
struct{
uint8_t bit0:1;
uint8_t bit1:1;
uint8_t bit2:1;
uint8_t bit3:1;
uint8_t bit4:1;
uint8_t bit5:1;
uint8_t bit6:1;
uint8_t bit7:1;
}pixels;
}UnTest;


BlkTest *pblkTest;
uint16_t u2Var1;
uint32_t u4Var2;
UnTest unTest;

int main(void){


pblkTest = (BlkTest *)test_array;
u2Var1 = pblkTest->m_u1Var1;
u4Var2 = pblkTest->m_u4Var2;

unTest.var = 0x11;


/*測試大小端 <<操作的
unTest.pixels.bit0 = 1;
unTest.pixels.bit4 = 1;

unTest.var <<= 1;
*/
while(1);
return 0;
}



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved