質問: **メンバ変数の中で最も大きな変数に境界調整は合わせられる**という理解で正しいでしょうか?? 構造体を作るときにいちいちoffsetofマクロでアライメントを確認するのが、非常に大変な作業なので、構造体を作るときにメンバ宣言のルールのようなものがありましたら、教えてください。 一応windows 64bitで実験しました。 > > [追記] > > > Linux 64 bit でもっとメンバ変数を多くして実験してみたところ、 double, float, int, short, unsigned char, char の順に下から大きい順に並べて構造体を作成してみたところ、gccコンパイラでのoffsetofの結果は、詰め物バイト数を最小にすることができました。 大きいバイト数から数えて(下から)、最大のバイト数の倍数(この場合、double型の8の倍数)になるように構造体の各メンバ変数を作成、調整をしていけば良さそうです。 どうしても、詰め物を入れざるを得ない状況(メンバ変数の全バイトが倍数にならない)で、詰め物を0byteにするのは実質、不可能なんでしょうか? 例えば、詰め物を0byteに出来るように、あえて使わないメンバ変数を作成し、0で初期化した場合、それをバイナリファイルへの出力としたら、中間に余計な0が入ってしまいます。 これでは、ダミー変数を用意しても詰め物と同じ結果になりませんか? > > [追記2] > > > メンバの順序 いろいろ出てきますが、**詰め物の最小化とメンバ変数の順序には関係性がありそうです。** maismakunさんやケイロニアンさんの回答をもとにすると、CPUとメモリ、メモリと記憶装置、の両方とも理想的な構造体を作成するのは、難しいと思われます 64bit CPUに話を絞るのですが、8^2なので、実行単位は8byteだと思います。 構造体全体のサイズを8の倍数にし、さらに詰め物を最小にするような構造体の作成はやや大変だと思います。 メンバ変数の順序以外に理想形に近づける方法は他にありますでしょうか?
回答: > > 直感的に詰め物がない構造体の方が良いと思われます。 > > > 何をもって「良い」とするかは意見が分かれますね。コンパイラーは無意味なことはしません。パディング(詰め物)は必要だから入れているのです。他の方も書かれている通り、ターゲットのCPUで最もパフォーマンスが引き出せるように調整してくれているのです(必ずしもプログラマーの意図通りではないかもしれませんが)。プログラムの中だけでしか使わない構造体なら、極端に搭載メモリが少ないマシンでもない限りそれが問題になることはほとんどないので、基本的にはコンパイラーに任せるのが一番良い方法です。 ところが**バイナリファイル**となると話は違ってきます。ファイルに出力するということは、別のマシンとのデータのやりとりに使うかもしれません。構造体のパディングは処理系依存なので、同じヘッダーファイルをインクルードしてコンパイルしても異なる配置になる可能性があります。 そうなると、あるマシンで出力したバイナリファイルを別のマシンで読ませるとまったくでたらめな内容になってしまうということが起こり得ます。あるいは、まったく同じプログラムを32bit版と64bit版のようにコンパイルを分けただけで、同じマシンで動作するにもかかわらずバイナリファイルの内容が異なるため互換性がなくなる、といった事態も起こり得ます。 > > バイナリファイルに対してのメモリの内容をそのまま読み書きするときは、構造体のメンバの並びや境界調整に気をつける > > > これの意図することはそういうことです。 理想を追い求めていらっしゃるようですが、何が理想なのかは状況に応じて変わってきます。コンパイラーは「CPUにとって理想的な配置」になるように自動的にパディングを入れています。今時の64bit向けコンパイラーなら、メンバ変数にdoubleなどの8バイトのデータ型があれば、構造体全体のサイズも自動で8の倍数になります(してくれます)。なるべくパディングが入らないように気をつけることは有意義ですが、やりすぎて関連する情報があちこちに散らばったりして読みづらくなったら、それはそれで「理想」とはほど遠い気がします。マシンサイクル単位でチューニングしなければならないような特殊な状況でもない限りは、利用する立場で考えて読みやすい構造体を設計することが、結果的には理想に近づくのだろうと思います。ファイルや通信などでのデータのやりとりでは、今のトレンドはXMLやJSONなどのテキスト形式ですから、**『バイナリファイルを使うことは構造体の理想以前にプログラムの仕様としてどうなの?』**という話になってしまいます。