[象の卵] 構造体/共用体のエンディアン?

この命題に取り組む前に、エンディアンの定義について確認しておくことにします。通常は、バイトオーダー、すなわちバイト単位での配置方式または配置順序のことですが、ここではnoocyteさんによる下記の定義を踏襲することにしましょう。

複数の要素 (普通はバイトまたはビット) からなるデータを表現するのに, 要素を並べる順序に自由度がある場合, 順序の選択肢の一つ一つがエンディアンである.

今回は、特にCには限らないようですので、まずはC++から見ていきましょう。C++の構造体は、クラスキーにstructを用いてい定義した一種のクラスのことです。したがって、Cの構造体とは違って、クラスのためのフルスペックの機能を使うことができます。

ということは、C++の構造体にはアクセス指定子(public, protected, private)を使ってもよいということです。C++のクラスのデータメンバは、基本的にはCと同じように記述した順にメモリに配置されますが、メンバとメンバの間にアクセス指定子が挟まった場合には、順序が未規定になります。

つまり、C++の構造体では、処理系が要素(メンバ)をメモリ上に並べる順序に自由度があるのです。自由度があるのなら、実際にどのような順序で並べられたのかを調べることは無意味ではなくなります。

まっさきに、思いつくのはoffsetofマクロでしょう。ところが、offsetofマクロはC互換構造体に対してでなければ通用しません。データメンバへのポインタを比較できればよいのですが、これも構文エラーになってしまいます。ただ、具体的なオブジェクトのそれぞれのメンバについてアドレスを取得することはできるので、この方法によって順序を知ることは可能です。

struct foo
{
  int a;
public:
  int b;
public:
  int c;
};
 
foo x;
char* pa = reinterpret_cast<char*>(&x.a);
char* pb = reinterpret_cast<char*>(&x.b);
char* pc = reinterpret_cast<char*>(&x.c);

ただ、この方法では、publicメンバの場合はよいのですが、それ以外のメンバを調べるのに困ります。もっとも、アクセスできないメンバの配置順序を知ったところで意味がありません。いや、下手に知れば、本来アクセスできないメンバに対して、アクセス指定を無視してアクセスする手段を提供するだけになってしまいます。

ところで、アクセス指定子を含む場合以外に、実はもうひとつメンバの配置順序に自由度があるケースが存在します。しかも、それはC++だけではなくCにもあります。ビットフィールドがそうです。

構造体のメンバにビットフィールドを使った場合、それらが上位のビットから割り付けられるか、下位のビットから割り付けられるかは決まっていません。バイトオーダーがリトルエンディアンであるからといって、ビットフィールドが下位ビットから割り付けられるとかそのような単純なものではありません。処理系によっては、コンパイルオプションで割り付け順序を切り替えられるものまで存在するのです。

ビットフィールドの場合も、並べる順序に自由度がありますから、これを調べることには意味があります。ビットフィールドの順序を静的に調べることは、コンパイル結果を解析する以外では不可能です。そのため、どうしても実行時に調べることになります。

ビットフィールドが何ビット目に割り付けられているかを知るには、構造体型のオブジェクト全体をゼロクリアしたあと、調べようとするビットフィールドに1を設定して、それをサーチしなければなりません。しかし、C++で、しかもC互換構造体ではないクラスにビットフィールドが含まれている場合には、その位置を調べる一般的な方法はなさそうです。

最後に共用体ですが、これは「共用体 エンディアン」のような検索フレーズで調べるのは、構造体のときとはまったく違う目的ではないかと思います。たとえば、次のような共用体を考えてみてください。

union bar
{
  int16_t w;
  int8_t b[2];
};

この場合に、b[0]にはwの上位8ビットが入るのか、下位8ビットが入るのかといったことではないでしょうか?

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/897
このエントリーを含むはてなブックマーク