5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C言語で構造体のサイズを調べる

Last updated at Posted at 2020-07-07

はじめに

構造体のサイズを調べていていろいろ気になったことがあるのでメモ。

環境

OS:Linux(ubuntu64bit)
コンパイラ:GCC

データ型のサイズの確認

まずデータ型のサイズを確認

size.c
# include <stdio.h>
# include <stdbool.h>
int main(void){
  printf("各データ型のサイズ\n" );
  printf("char  :%zu\n",sizeof(char) );
  printf("short :%zu\n",sizeof(short) );
  printf("int   :%zu\n",sizeof(int) );
  printf("long  :%zu\n",sizeof(long) );
  printf("float :%zu\n",sizeof(float) );
  printf("double:%zu\n",sizeof(double) );
  printf("bool  :%zu\n",sizeof(bool) );
  return 0;
}

結果

各データ型のサイズ
char  :1
short :2
int   :4
long  :8
float :4
double:8
bool  :1

ポインタ型のサイズも確認

pointa.c
# include <stdio.h>
int main(void){
  printf("ポインタ型のサイズ\n" );
  printf("char*  :%zu\n",sizeof(char*) );
  printf("void*  :%zu\n",sizeof(void*) );

結果

ポインタ型のサイズ
char*  :8
void*  :8

長くなるので全部は書きませんが、ポインタ型はすべて8バイトでした。

構造体のサイズ

kouzoutai.c
# include <stdio.h>

typedef struct{
  short s;
  long l;
}kouzoutai;

int main(void){
  kouzoutai x;
  printf("x  :%zu\n",sizeof(x) );
  return 0;
}

kouzoutaiであるxのサイズを見てみましょう。
short(2) + long(8) = 10バイトになるでしょうか。
結果

x:16

16バイトになりました。なぜでしょうか。
これはパティングが確保されているためです。

パティングとは

メンバはある一定の規則に基づいてメモリに配置されています。
各データサイズの倍数に配置されます。
これをアライメントというそうです。
(64bitの場合)
char :1の倍数
short:2の倍数
int :4の倍数
long :8の倍数

今回の場合、

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
short パティング long
※0は2の倍数ではありませんがぴったり0から配置されることはまずないので1000から始まってると考えてください。

このように2~7にパティングが入っているため10バイトではなく16バイトになるということです。
ちなみにパティングとは水増し、詰め物という意味らしいです。
なぜパティングというものがあるのか、それはメモリアクセスを効率よく行うためです。
データサイズの倍数という一定の規則に基づいて配置してあるためどこにあるか探しやすいということですね。

kouzoutai.c
# include <stdio.h>

typedef struct{
  short s;
  long l;
}kouzoutai;

int main(void){
  kouzoutai x;
  printf("x  :%zu\n",sizeof(x) );
  printf("%p\n",&x.s );
  printf("%p\n",&x.l );
  return 0;
}

先程のkouzoutai.cに構造体のメンバのアドレスを出力する文を足しました。

x  :16
short sのアドレス:0x7ffebf1d8c90
long  lのアドレス:0x7ffebf1d8c98

アドレスの下一桁目を見るとshortが0,longが8から始まっているのがわかると思います。

もう一点だけ見てみます。

kouzoutai2.c
# include <stdio.h>

typedef struct{
  long l;
  long a;
  int s;
}kouzoutai2;

int main(void){
  kouzoutai2 y;
  printf("y:%zu\n",sizeof(y) );
  return 0;
}

今回はすべてデータサイズの倍数に収まりそうです。
kouzoutai2のサイズは
long(8) + long(8) + int(4) = 20 になるでしょうか。

結果

y:24
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
long long int パティング

intで終わらず23までパティングが確保されました。
パティングは構造体内のメンバで最も大きい型に合うように確保されるようです。
この場合はkouzoutai2の中で最も大きいメンバはlongなので20以上の8の倍数で24バイトになるようにパティングが確保されます。

union(共用体)

kyouyoutai.c
# include <stdio.h>

typedef struct{
  union{
    char c;
    long s;
  }q;
  long l;
  int i;
}kouzoutai3;

int main(void){
  kouzoutai3 z;
  printf("z:%zu\n",sizeof(z) );
  return 0;
}

単純にメンバのサイズを数えると32バイトですが、

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
char パティング long long int パティング

この結果は

z:24

になります。

これはunion内のcharとlongが同じアドレスに配置されることから起こります。
unionのイメージとしては以下のような感じです。

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
long long int パティング
char

サイズの確認は以上です。

気になる点

kouzoutai2.c
# include <stdio.h>

typedef struct{
  long l;
  long a;
  int s;
}kouzoutai2;

int main(void){
  kouzoutai2 y;
  printf("lのアドレス:%p\n",&y.l );
  printf("aのアドレス:%p\n",&y.a );
  printf("sのアドレス:%p\n",&y.s );

  int p;
  printf("pのアドレス:%p\n",&p );
  return 0;
}

これを実行すると

lのアドレス:0x7ffe6cf682f0
aのアドレス:0x7ffe6cf682f8
sのアドレス:0x7ffe6cf68300
pのアドレス:0x7ffe6cf682ec

pのアドレスがlのアドレスの前に来ているのはなぜなんでしょう。
普通ならpのアドレスは「0x7ffe6cf68304」になると思うんですが...。
気になります。

5
5
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?