4

之前有人问过这个问题,但是当移动到具有相反字节序的平台时(在这种情况下从大到小),我仍然对如何处理位域结构感到有些困惑。所以如果我有这个:

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 进行包装,但我不想在这里把东西弄得乱七八糟。

4

3 回答 3

2

您需要担心的不仅仅是字节序问题。请注意,位域在内存中的布局细节并未由 C 标准定义,这意味着两个编译器可以生成不同的结果,即使它们针对的是具有相同字节序的平台。有些人可能会将列出的第一个位域视为最低地址位,而其他人可能会将其视为最高地址位。

您有两种选择来解决此问题。

首先是健康剂量#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 方法,但是您可以避免大多数可移植性问题,因为您明确指定了所有内容的位顺序和字节顺序。

于 2012-08-22T16:17:08.633 回答
2

我会保留您最初的内容,但会颠倒word引用之前的字节顺序(如果需要)。

于 2012-08-22T16:07:39.617 回答
1

我想说以下结构是不可移植的,因为它不会将内存中结构使用的位模式从小端更改为大端或 verse vica:

typedef struct
{
    unsigned short a :5;
    unsigned short b :1;
    unsigned short c :5;
    unsigned short d :5;
} protocol_type;

证明:

大端内存布局:

 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            ]

小端内存布局:

 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;

要在切换字节序时将位模式保留在内存中,只需像这样修改它:

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);
于 2012-08-22T15:49:55.557 回答