之前曾问过这个问题,但是当我转移到具有相反字节序的平台时(在这种情况下,从大到小),我仍然对如何处理位域结构感到困惑。如果我有这个:
typedef struct
{
unsigned short a :5;
unsigned short b :1;
unsigned short c :5;
unsigned short d :5;
} protocol_type;
typedef union
{
protocol_type cmd;
unsigned short word;
}protocol_cmd_type;
这样处理这个问题的正确方法是什么?
typedef struct
{
unsigned short d :5;
unsigned short c :5;
unsigned short b :1;
unsigned short a :5;
} protocol_type;
typedef union
{
protocol_type cmd;
unsigned short word;
}protocol_cmd_type;
还是其他什么?
这就是我所做的,但它没有给出我期待的结果。但是这个代码还有其他问题,所以我不确定上面是否真的错了。希望能在这里获得洞察力,这样我就可以将这部分列入清单。
实际上我需要让代码在两个平台上都能正常工作,所以我要把它们包装在#defines周围,但我不想把这些东西弄得乱七八糟。
答案 0 :(得分:2)
我会保留你原来的内容,但在引用之前反转word
的字节顺序(如果需要)。
答案 1 :(得分:2)
在这里,您需要担心的更多是关于endianess问题。请注意,C标准并未定义如何在内存中布置位域的详细信息,这意味着两个编译器可以生成不同的结果,即使它们是针对具有相同endianess的平台。有些可能会将列出的第一个位字段视为最低地址位,而其他位可能会将其视为最高地址位。
您有两种方法可以解决此问题。
第一种是健康剂量#ifdef
:
typedef struct
{
#ifdef CPU_IS_BIG_ENDIAN
unsigned short a :5;
unsigned short b :1;
unsigned short c :5;
unsigned short d :5;
#else
unsigned short d :5;
unsigned short c :5;
unsigned short b :1;
unsigned short a :5;
#endif
} protocol_type;
这导致了凌乱的结构定义,但允许其余代码保持干净。由于您具有跨越字节边界的字段,因此您必须为每个目标体系结构/平台提供新的结构定义(可能通过反复试验)。如果您必须支持多个编译器,这些编译器对同一平台的位域命名方式不同,那么您的定义将变得更加复杂。
另一种选择是完全避免使用位域并改为使用位掩码:
typedef unsigned char protocol_type[2];
#define extract_a(x) ((x[0] & 0xF8) >> 3)
#define extract_b(x) ((x[0] & 0x04) >> 2)
#define extract_c(x) (((x[0] & 0x03) << 3) | ((x[1] & 0xE0) >> 5))
#define extract_d(x) ((x[1] & 0x1F))
这需要使用getter / setter方法,但是你要避免大多数可移植性问题,因为你明确指定了所有内容的位和字节顺序。
答案 2 :(得分:1)
我认为以下结构不可移植性,不会改变内存中结构使用的位模式,从小端到大端或者维也纳vica:
typedef struct
{
unsigned short a :5;
unsigned short b :1;
unsigned short c :5;
unsigned short d :5;
} protocol_type;
证明:
Big endian内存布局:
d4 d3 d2 d1 d0 c4 c3 c2 c1 c0 b0 a4 a3 a2 a1 a0
<- byte 1 -> <- byte 0 ->
MSB LSB MSB LSB
[ address 1 ] [ address 0 ]
Little endian内存布局:
c1 c0 b0 a4 a3 a2 a1 a0 d4 d3 d2 d1 d0 c4 c3 c2
<- byte 0 -> <- byte 1 ->
MSB LSB MSB LSB
[ address 1 ] [ address 0 ]
从此我不知道如何重新排序a
,b
,c
和d
以在小端和大端上形成相同的位模式机器。原因是结构的成员c
越过字节边界。
以下结构可以是便携式的:
typedef struct
{
unsigned short e :5;
unsigned short f :3;
unsigned short g :3;
unsigned short h :5;
} protocol_type;
在切换endianess时保持位模式在内存中只需修改它:
typedef struct
{
unsigned short g :3;
unsigned short h :5;
unsigned short e :5;
unsigned short f :3;
} protocol_type;
OP问题的一个可能解决方案是按以下方式修改结构:
typedef struct
{
#if defined(BIGENDIAN)
unsigned short a :5;
unsigned short b :1;
unsigned short c0 :2;
unsigned short c1 :3;
unsigned short d :5;
#elif defined(LITTLEENDIAN)
unsigned short c1 :3;
unsigned short d :5;
unsigned short a :5;
unsigned short b :1;
unsigned short c0 :2;
#else
#error "endianess not supported"
#endif
} protocol_type;
#define pt_c(pt) (pt.c0 & (pt.c1 << 2))
foo(void)
{
protocol_type pt;
... /* some assignment to pt ... */
/* to then access the original value of member c use the macro */
unsigned short c = pt_c(pt);