はじめに
構造体のサイズを調べていていろいろ気になったことがあるのでメモ。
環境
OS:Linux(ubuntu64bit)
コンパイラ:GCC
データ型のサイズの確認
まずデータ型のサイズを確認
# 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
ポインタ型のサイズも確認
# 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バイトでした。
構造体のサイズ
# 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バイトになるということです。
ちなみにパティングとは水増し、詰め物という意味らしいです。
なぜパティングというものがあるのか、それはメモリアクセスを効率よく行うためです。
データサイズの倍数という一定の規則に基づいて配置してあるためどこにあるか探しやすいということですね。
# 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から始まっているのがわかると思います。
もう一点だけ見てみます。
# 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(共用体)
# 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 |
サイズの確認は以上です。
気になる点
# 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」になると思うんですが...。
気になります。