簡體   English   中英

在位域結構上轉換Endianess

[英]Converting Endianess on a bit field structure

我需要將位域結構從little-endian轉換為big-endia架構。 如果我只是交換結構元素,那么在字節邊界中會出現問題的最佳方法是什么?

Ex結構是:

struct {
    unsigned int    b1:1;
    unsigned int    b2:8;
    unsigned int    b3:7;
    unsigned int    b4:8;
    unsigned int    b5:7;
    unsigned int    b6:1;
}; 

您可以使用32位整數,並使用and-和bitshift運算符從中提取信息。 有了這個,你可以簡單地使用htonl(host-to-network,long)。 網絡字節順序是大端。

這不會像位字段那樣優雅,但至少你會知道你擁有什么,而不必擔心編譯器填充你的結構。

處理器字節順序與位字段排序無關。 在同一台計算機上使用兩個編譯器很可能使用相反的位域排序。 所以,鑒於此:

union {
    unsigned char x;
    struct {
        unsigned char b1 : 1;
        unsigned char b2 : 7;
    };
} abc;
abc.x = 0;
abc.b1 = 1;
printf( "%02x\n", abc.x );

除非您碰巧有詳細的文檔,否則了解是否打印出01或80的唯一方法就是嘗試它。

在將代碼從MIPS移植到Linux / x86的項目中,我們確實喜歡這樣。

struct {

#ifdef __ONE_ENDIANESS__
    unsigned int    b1:1;
    unsigned int    b2:8;
    unsigned int    b3:7;
    unsigned int    b4:8;
    unsigned int    b5:7;
    unsigned int    b6:1;
#define _STRUCT_FILLED
#endif /* __ONE_ENDIANESS__ */

#ifdef __OTHER_ENDIANESS__
    unsigned int    b6:1;
    unsigned int    b5:7;
    unsigned int    b4:8;
    unsigned int    b3:7;
    unsigned int    b2:8;
    unsigned int    b1:1;
#define _STRUCT_FILLED
#endif /* __OTHER_ENDIANESS__ */

};

#ifndef _STRUCT_FILLED
#  error Endianess uncertain for struct
#else
#  undef _STRUCT_FILLED
#endif /* _STRUCT_FILLED */

__ONE_ENDIANESS____OTHER_ENDIANESS__適用於我們使用的編譯器,因此您可能需要查看哪個適合您...

那里有兩個16位部分(前三個字段,后三個字段是16位)。

這只是65536個條目。 所以有一個查找表,保存字段的位反轉版本。 將結構包裝在另一個具有兩個16位字段的結構的聯合中,以使其更容易?

像(未經測試,我不在C編譯器附近):

union u {
    struct {
        unsigned int    b1:1;
        unsigned int    b2:8;
        unsigned int    b3:7;
        unsigned int    b4:8;
        unsigned int    b5:7;
        unsigned int    b6:1;
     } bits;
     struct {
        uint16 first;
        uint16 second;
     } words
} ;

unit16 lookup[65536];

/* swap architectures */

void swapbits ( union u *p)
{
   p->words.first = lookup[p->words.first];
   p->words.second = lookup[p->words.second];
}

查找表的人口留給讀者的練習:)

但是,請仔細閱讀您的編譯器文檔。 我不確定C標准是否要求結構符合一個單詞(盡管我希望大多數編譯器都這樣做)。

您希望在通道(文件或網絡)和結構之間執行此操作。 我的首選做法是通過以已知表示形式構建文件緩沖區的寫代碼和匹配反轉該轉換的讀取代碼來隔離文件I / O與結構。

您的具體示例特別難以猜測,因為位域定義為unsigned int,而sizeof(unsigned int)特別不可移植。

假設作為SWAG的sizeof(int)==4然后獲取指向結構的指針並重新編寫單個字節可能會得到你想要的答案。

為不同平台定義結構的技巧可能有用,但在示例中,您引用的是字節邊界沒有干凈的中斷,因此不可能在另一個平台上生成等效的一個平台而不進行拆分一個或多個字段分成兩部分。

交換字節應該足夠了。 一個字節內的位位置在大端和小端都是相同的。
例如:

char* dest = (char*)&yourstruct;
unsigned int orig = yourstruct;
char* origbytes = (char*)&orig;
dest[0] = origbytes[3];
dest[1] = origbytes[2];
dest[2] = origbytes[1];
dest[3] = origbytes[0];

當物理布局很重要時,不應使用位字段,因為它是實現定義的,填充較大的字的順序。

為了實現這一目標,我終於得到了一個解決方案(一些從上面的epatel解決方案中獲得的解決方案)。 這是我從x86轉換為Solaris SPARC。

我們需要首先交換傳入的結構,然后以相反的順序讀取元素。 基本上在查看結構是如何對齊之后,我看到字節順序和位排序中的字節順序都發生了變化。 這是一個偽代碼。

struct orig
{    
    unsigned int    b1:1;
    unsigned int    b2:8;
    unsigned int    b3:7;
    unsigned int    b4:8;
    unsigned int    b5:7;
    unsigned int    b6:1;
};

struct temp
{    
    unsigned int    b6:1;
    unsigned int    b5:7;
    unsigned int    b4:8;
    unsigned int    b3:7;
    unsigned int    b2:8;
    unsigned int    b1:1;
}temp;


func (struct orig *toconvert)
{
    struct temp temp_val;
    //Swap the bytes
    swap32byte((u32*)toconvert);
    //Now read the structure in reverse order - bytes have been swapped
    (u32*)&temp_val = (u32 *)toconvert;
    //Write it back to orignal structure
    toconvert->b6=temp_val.b6;
    toconvert->b5=temp_val.b5;
    toconvert->b4=temp_val.b4;
    toconvert->b3=temp_val.b3;
    toconvert->b2=temp_val.b2;
    toconvert->b1=temp_val.b1;

}

經過一些實驗后,我發現這種方法只有在元素完全填滿結構時才有效,即沒有未使用的位。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM