我需要将位域结构从小端架构转换为大端架构。如果我只是交换结构元素,那么最好的方法是什么,因为字节边界会出现问题。
防爆结构为:
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;
};
我需要将位域结构从小端架构转换为大端架构。如果我只是交换结构元素,那么最好的方法是什么,因为字节边界会出现问题。
防爆结构为:
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(主机到网络,长)。网络字节顺序是大端。
这不会像位域那样优雅,但至少你会知道你拥有什么,并且不必担心编译器会填充你的结构。
处理器字节序与位字段排序无关。同一台计算机上的两个编译器很可能对位域使用相反的顺序。所以,鉴于此:
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;
}
经过一些实验,我发现这种方法只有在元素完全填充结构时才有效,即没有未使用的位。