【C言語】構造体を作る上でのアライメントのお話。そもそもアライメントとは...

  • URLをコピーしました!

こんにちは,ちゆりです。

今回は上司に

上司

構造体を作る上ではアライメントを意識しなきゃダメでしょ。自分本位に欲しい物だけを詰めた構造体を作ってはダメよ。ダメダメ。

と指摘され,C言語におけるアライメントについて考えるいい機会になった。

目次

アライメントとは

そもそもアライメントとは何なの。状態の僕でありましたが,せめて意味ぐらいは知っておこうと思い,辞書を引き英語の意味を調べてみました。

アライメント・・・「並べる,整列,比較,調整」などの意味で使われる。

だがしかし,今回はこんなことはどうでも良いのだ。

C言語におけるアライメントとは

まぁ。「アライメント」という意味的に何かを調整してくれるのだろう。
構造体を例に説明するとわかりやすいです。

例えばこんな構造体があったとしましょう。

  typedef struct
  {
    int int1;
    double double1;
    char char1;
    double double2;
  }STRUCT1;

そしてこの構造体をsizeof演算子でサイズを見てみるといくつになるでしょうか。
(ちなみに,僕の環境はsizeof(int)4バイト,sizeof(char)1バイト,sizeof(double)が,8バイトでした。)

じゃ,もう簡単ですね。

int + double + char + double = 4 + 8 + 1 + 8 = 21バイト

残念。不正解です。21バイトとはなりませんでした。
僕の環境では,なぜか24バイトとなりました。(環境によって異なる場合があります。)

では,どうしてこうなってしまうのか。実験していきましょう。

アライメントの計算

サクッとサンプル関数を作っていきます。

#include <stdio.h>
#include <stdint.h>

typedef struct
{
  int int1;
  double double1;
  char char1;
  double double2;
}STRUCT1;

int main()
{
  STRUCT1 st1;
  printf("st1 size=%d\n", sizeof(STRUCT1));

  printf("st1     %p\n", &st1);
  printf("int1    %p\n", &st1.int1);
  printf("double1 %p\n", &st1.double1);
  printf("char1   %p\n", &st1.char1);
  printf("double2 %p\n", &st1.double2);

  return 0;
}

僕の環境では,以下の結果となりました。

st1 size=24
st1      0x7ffacad0
int1     0x7ffacad0
double1  0x7ffacad4
char1    0x7ffacadc
double2  0x7ffacae0

intは4byte,doubleは8byte,そしてcharにも4byteのアドレスが割り当てられているように見えますね。

charが1バイトのはずが4バイトとなっているために構造体サイズが24バイトとなっていました。

この理由は,CPUの都合によって,コンパイラが適当に境界調整(アライメント)を行い,構造体にパディングを挿入しているからなのです。

パディングとは

僕の環境ではintdoubleは4の倍数のアドレスに配置されるようで,charも4の倍数のアドレスに格納され,4バイト ー char(1バイト) = 3バイトがパディングされていたようです。

構造体アライメント図
構造体アライメント図

どうしてパディングされるのか

どうしてパディングされるのかイマイチピンときませんが...

(構造体の)サイズが小さい方が良くない??
パディングによって構造体アクセスに影響があるのでは??

なんて思ってましたけど,パディングされるのにもワケがあるのでしょう。そのワケとは..?

CPUのメモリへのアクセス高速化なんです。
高速化の仕組みについては今回割愛しますが...

奇数よりも偶数で,かつ,4バイト区切りで構造体を作成しなさい。というアドバイスです。
それを,意識すれば「お。こいつC言語分かっているな。」と一目置かれるでしょう(多分...)

最後に

構造体を作成する上で,特にchar型なんかは配列で作成すると思いますが,char[110]という配列よりもchar[120]という配列のほうがセンスが良いです。たとえ,配列に100文字しか格納されないとしても,4の倍数で多めに配列を取ってあげましょう。

ちなみに

パディングの関係で以下2つの構造体のサイズは一緒になります。

typedef struct
{
  int int1;
  double double1;
  char char1;
  double double2;
}STRUCT1;

typedef struct
{
  int int2;
  double double3;
  char char2[4];
  double double4;
}STRUCT2;


なので,パディングを意識しないと予期しない構造体サイズになっていたなんてこともあり得ますね。

C言語おすすめ書籍

僕のC言語のおすすめ書籍を紹介しておきます。難易度はやや高めなので入門書として購入するのはあまりおすすめできませんが,C言語をある程度勉強された方には非常にタメになる一冊です。これさえ理解できれば怖いものなしです。ぜひ。

★次に読んでほしい記事

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (2件)

  • もう少し厳密に言うと、char型が境界調整されたわけではなく、後続のdouble型の影響で3バイトのパディングが発生します。
    このため、char char1;の変数の後ろにchar char2;を追加した場合、2バイトのパディングが発生します。char2のアドレスはchar1の1番地隣に配置されます。

    特に見た目上のレイアウトを気にしないのであれば、境界調整の強い順(大きい順。long long,long,short,char)に並べて、最後に予備項目としてサイズ調整をすると楽ちんです。

    ご参考まで。

    • なるほど。そうだったんですね!
      深い部分を教えていただきありがとうございます。
      C言語使いとしてしっかり頭に入れておきます

コメントする

目次