[英]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.