简体   繁体   中英

Converting Endianess on a bit field structure (again)

This question was asked before, but I'm still a bit confused on how to deal with bitfield structures when moving to a platform with the opposite endianness (big to little in this case). So if I have this:

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;

Is the correct way to deal with this, like this?

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;

Or something else?

That's what I did, but it's not giving results I was expecting. However there are other issues with this code, so I'm not sure if the above was actually wrong or not. Hoping to get insight here so I can knock this part off the list.

In fact I need to have the code work on both platforms still, so I'd be wrapping things around #defines, but I didn't want to clutter things here.

我会保留你原来的内容,但在引用之前反转word的字节顺序(如果需要)。

You have more to worry about than endianess issues here. Be aware that the details of how bitfields are laid out in memory is not defined by the C standard, meaning that two compilers can generate different results, even if they are targeting platforms with the same endianess. Some may treat the first bitfield listed as the lowest-address bit, and others may treat it as the highest-address bit.

You have two options for resolving this.

The first is with a healthy dose of #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;

This leads to a messy structure definition, but allows the rest of the code to stay clean. Since you have fields that cross the byte boundary, you'll have to essentially come up with a new structure definition (possibly by trial and error) for every target architecture/platform. If you have to support multiple compilers that order bitfields differently for the same platform, then your definition will become even more complex.

The other option is to avoid bitfields altogether and use bitmasks instead:

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))

This requires the use of getter/setter methods, but you avoid most of the portability problems since you're explicitly specifying both the bit and byte orders for everything.

I'd say the following structure is not portable in terms of not changing the bit pattern used by the structure in memory from little to big endian or verse vica:

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

Proof:

Big endian memory layout:

 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 memory layout:

 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             ]

From this I see no way how to re-order a , b , c , and d to form the same bit pattern on either little and big endian machines. The reason for this is the fact that the structure's member c crosses the byte boundary.


The following structure might be made portable:

typedef struct
{
    unsigned short e :5;
    unsigned short f :3;
    unsigned short g :3;
    unsigned short h :5;
} protocol_type;

To keep the bit pattern in memory when switching endianess just mod it like so:

typedef struct
{
    unsigned short g :3;
    unsigned short h :5;
    unsigned short e :5;
    unsigned short f :3;
} protocol_type;

A possible solution to the OP's problem would be to mod the structure the following way:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM