在位域结构上转换Endianess

时间:2009-04-07 06:58:08

标签: c data-structures endianness

我需要将位域结构从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;
}; 

8 个答案:

答案 0 :(得分:8)

您可以使用32位整数,并使用and-和bitshift运算符从中提取信息。有了这个,你可以简单地使用htonl(host-to-network,long)。网络字节顺序是大端。

这不会像位字段那样优雅,但至少你会知道你拥有什么,而且不必担心编译器填充你的结构。

答案 1 :(得分:6)

处理器字节顺序与位字段排序无关。在同一台计算机上使用两个编译器很可能使用相反的位域排序。所以,鉴于此:

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的唯一方法就是尝试。

答案 2 :(得分:5)

在将代码从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__适用于我们使用的编译器,因此您可能需要查看哪种适合您...

答案 3 :(得分:1)

那里有两个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标准是否要求结构符合一个单词(尽管我希望大多数编译器能够这样做)。

答案 4 :(得分:1)

您希望在频道(文件或网络)和结构之间执行此操作。我的首选做法是通过编写代码来隔离文件I / O,这些代码以已知的表示形式构建文件缓冲区,并匹配反转该转换的读取代码。

您的具体示例特别难以猜测,因为位域定义为无符号整数,sizeof(unsigned int)特别不可移植。

假设作为SWAG sizeof(int)==4然后获取指向结构的指针并重新编写单个字节可能会得到你想要的答案。

为不同平台定义结构的技巧可能可以工作,但在示例中,您引用的是字节边界没有干净的中断,因此不太可能生成相当于另一个平台,而不将一个或多个字段分成两部分。

答案 5 :(得分:0)

交换字节应该足够了。一个字节内的位位置在大端和小端都是相同的 例如:

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];

答案 6 :(得分:0)

当物理布局很重要时,不应该使用位字段,因为它是实现定义的,填充较大的字的顺序。

答案 7 :(得分:-3)

为了实现这一目标,我终于得到了一个解决方案(一些从上面的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;

}

经过一些实验后,我发现这种方法只有在元素完全填满结构时才有效,即没有未使用的位。