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標準で定義されていないことに注意してください。つまり、2つのコンパイラが同じエンディアンのプラットフォームをターゲットにしている場合でも、異なる結果を生成できます。リストされている最初のビットフィールドを最下位アドレスビットとして扱うものもあれば、最上位アドレスビットとして扱うものもあります。

これを解決するには、2つのオプションがあります。

最初は健康的な用量で#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;

これにより、構造の定義が乱雑になりますが、残りのコードをクリーンに保つことができます。バイト境界を越えるフィールドがあるため、基本的に、すべてのターゲットアーキテクチャ/プラットフォームに対して(おそらく試行錯誤によって)新しい構造定義を考え出す必要があります。同じプラットフォームでビットフィールドの順序が異なる複数のコンパイラをサポートする必要がある場合、定義はさらに複雑になります。

もう1つのオプションは、ビットフィールドを完全に回避し、代わりにビットマスクを使用することです。

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 に答える
1

次の構造は、メモリ内の構造で使用されるビット パターンをリトル エンディアンからビッグ エンディアンに変更しない、または vice 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方法bcわかりません。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 に答える