読者です 読者をやめる 読者になる 読者になる

TomoProgの技術書

底辺プログラマーが達人プログラマーになるまで

C言語のstruct型を使ってたら謎の現象に遭遇した

皆さん
こんにちは、こんばんは
TomoProgです。

前の記事に星をつけてくださった方ありがとうございます!!
次は20記事を目標に頑張っていきます!!

今回はPythonを離れて、C言語のstruct型で疑問に思ったことを書いていこうと思います。

謎の現象に遭遇

C言語で以下のようなコードを書いてみると、謎の現象に遭遇しました。

#include <stdio.h>

typedef struct st_a
{
    int num_a;
    char char_a;
    int num_b;
}ST_A;

int main(void)
{
    printf("sizeof(int):%lu\n", sizeof(int));
    printf("sizeof(char):%lu\n", sizeof(char));
    printf("sizeof(ST_A):%lu\n", sizeof(ST_A));
    return 0;
}
実行結果:
sizeof(int):4
sizeof(char):1
sizeof(ST_A):12

あれ?
構造体の大きさが12バイト?
定義した構造体のメンバはint型2つとchar型1つだから合計で「9」バイトになるんじゃないの?

というわけで今日は構造体の大きさの謎に迫ってみようと思います。

バイト境界、アライメント、パディング

とりあえずgoogle先生に聞いてみたらこのサイトを教えてくれたので、勉強させていただきました。
C言語編 - 構造体のサイズとアライメント

  • メモリに変数が配置される時は「バイト境界」と言われる区切りに合わせて配置される。
  • メンバ間に空間が出来ることを構造体の「アライメント(整列)」と呼ぶ。
  • メンバ間の空白を「パディング」という。

なるほど。
今回の内容もメモリに配置されるときにアライメントされているので、メンバ間に空白があるということか。

メンバ間のパディングを確かめる

メンバのそれぞれの先頭アドレスを調べてパディングを確かめてみます。

#include <stdio.h>

typedef struct st_a
{
    int num_a;
    char char_a;
    int num_b;
}ST_A;

int main(void)
{
    ST_A st_a;

    printf("num_a  address:[%p]\n", &st_a.num_a);
    printf("char_a address:[%p]\n", &st_a.char_a);
    printf("num_b  address:[%p]\n", &st_a.num_b);

    return 0;
}
実行結果(実行毎に異なります):
num_a  address:[0x7fffc215c060]
char_a address:[0x7fffc215c064]
num_b  address:[0x7fffc215c068]

メンバが4バイトずつ配置されていることが確認出来ました!!

また、char型は1バイト、int型は4バイトだったので、定義した構造体はメモリ上では下記のように配置されてそうです!!

メモリ配置:
[0x7fffc215c060] → num_a(先頭アドレス)
[0x7fffc215c061] → num_a
[0x7fffc215c062] → num_a
[0x7fffc215c063] → num_a
[0x7fffc215c064] → char_a(先頭アドレス)
[0x7fffc215c065] → パディング
[0x7fffc215c066] → パディング
[0x7fffc215c067] → パディング
[0x7fffc215c068] → num_b(先頭アドレス)
[0x7fffc215c069] → num_b
[0x7fffc215c06A] → num_b
[0x7fffc215c06B] → num_b

まとめ

構造体のメンバはバイト境界に応じてメモリ上に配置される。
そのため、単純に定義したメンバの型の数で計算した結果になるとは限らない。

これからはstructを使うときはバイト境界に気をつけて定義しようと思います。
それではまた。

GitHub
https://github.com/TomoProg

Twitter
TomoProg (@TomoHelloxxx) | Twitter