C言語,構造体のアライメントとパディングについて

スポンサーリンク

鍛錬 836

C言語,構造体のアライメントとパディングについて

CPUのメモリへのアクセスを高速化するために、コンパイラはアライメントに従って構造体メンバの間にパディングを行います。

スポンサーリンク

実際に確認

実際にプログラムを作成してパディングが行われる様子を確認します。

以下は、私が使用しているコンパイラにおける、変数のサイズです。
(変数のサイズは、コンパイラ等により異なります。)

サイズ
char 1 バイト
int 4 バイト
double 8 バイト

上に記した変数のサイズを参考にして、以下の構造体 st について考えます。

struct ST {
	char c;		// 1バイト
	int i;		// 4バイト
	double d;	// 8バイト
};
struct ST st;

上に記した構造体 st の構造体メンバを合計すると、13 バイトになります。

本当に 13 バイトであるのかを確かめるため、実際にプログラムを作成して構造体 st のサイズを確認します。

以下は、構造体 st について、構造体のサイズと構造体メンバのアドレスを確認するプログラムです。

// include
#include <stdio.h>

// main
int main(void)
{
	struct ST {
		char c;		// 1バイト
		int i;		// 4バイト
		double d;	// 8バイト
	};
	struct ST st;
	
	printf("char   のサイズ = %ld\n", sizeof(st.c));
	printf("int    のサイズ = %ld\n", sizeof(st.i));
	printf("double のサイズ = %ld\n", sizeof(st.d));
	printf("---------------------------\n");
	printf("構造体 st のサイズ = %ld\n", sizeof(st));
	
	printf("\n");
	
	printf("char   のアドレス = %p\n", &st.c);
	printf("int    のアドレス = %p\n", &st.i);
	printf("double のアドレス = %p\n", &st.d);
	
	return 0;
}

以下は、上記のプログラムを実行しています。

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ arch
x86_64
***@ubuntu:~/***/test/c$ gcc -dumpversion
7
***@ubuntu:~/***/test/c$ gcc -dumpmachine
x86_64-linux-gnu
***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra sample.c -o sample
***@ubuntu:~/***/test/c$ ./sample
char   のサイズ = 1
int    のサイズ = 4
double のサイズ = 8
---------------------------
構造体 st のサイズ = 16

char   のアドレス = 0x7ffd36674200
int    のアドレス = 0x7ffd36674204
double のアドレス = 0x7ffd36674208

上記に示した通り、構造体 st のサイズは 13 バイトではなく 16 バイトとなりました。

16 バイトとなった理由は、コンパイラがアライメントを 4 として、構造体メンバの「char c」と「int i」の間に 3 バイトをパディングしたためです。

構造体 st の構造体メンバは、以下の順序でメモリに配置されています。

構造体 st のメモリ配置

struct ST {
	char c;		// 1バイト
	int i;		// 4バイト
	double d;	// 8バイト
};
struct ST st;

0:char c (1 バイト)
1:パディング
2:パディング
3:パディング
4:int i (4 バイト)
5:int i
6:int i
7:int i
8:double d (8 バイト)
9:double d
10:double d
11:double d
12:double d
13:double d
14:double d
15:double d

スポンサーリンク

同じ構造体メンバでも順序によりサイズが異なる

構造体を記述する際の順序を変えることにより、メモリに配置される順序も変化するため、同じ構造体メンバでもパディングが行われる場合と行われない場合があります。

以下の構造体 st_1 と st_2 では、構造体メンバは同じですが構造体内での順序が異なります。

struct ST_1 {
	char c_1;	// 1バイト
	int i;		// 4バイト
	char c_2;	// 1バイト
	char c_3;	// 1バイト
	char c_4;	// 1バイト
	double d;	// 8バイト
};
struct ST_1 st_1;
struct ST_2 {
	char c_1;	// 1バイト
	char c_2;	// 1バイト
	char c_3;	// 1バイト
	char c_4;	// 1バイト
	int i;		// 4バイト
	double d;	// 8バイト
};
struct ST_2 st_2;

以下は、上記2つの構造体について、サイズを確認するプログラムです。

// include
#include <stdio.h>

// main
int main(void)
{
	struct ST_1 {
		char c_1;	// 1バイト
		int i;		// 4バイト
		char c_2;	// 1バイト
		char c_3;	// 1バイト
		char c_4;	// 1バイト
		double d;	// 8バイト
	};
	struct ST_1 st_1;
	
	struct ST_2 {
		char c_1;	// 1バイト
		char c_2;	// 1バイト
		char c_3;	// 1バイト
		char c_4;	// 1バイト
		int i;		// 4バイト
		double d;	// 8バイト
	};
	struct ST_2 st_2;
	
	printf("構造体 st_1 のサイズ --> %ld\n", sizeof(st_1));
	printf("構造体 st_2 のサイズ --> %ld\n", sizeof(st_2));
	
	printf("\n");
	
	printf("st_1.c_1    のアドレス = %p\n", &st_1.c_1);
	printf("st_1.i      のアドレス = %p\n", &st_1.i);
	printf("st_1.c_2    のアドレス = %p\n", &st_1.c_2);
	printf("st_1.c_3    のアドレス = %p\n", &st_1.c_3);
	printf("st_1.c_4    のアドレス = %p\n", &st_1.c_4);
	printf("st_1.double のアドレス = %p\n", &st_1.d);
	
	printf("\n");
	
	printf("st_2.c_1    のアドレス = %p\n", &st_2.c_1);
	printf("st_2.c_2    のアドレス = %p\n", &st_2.c_2);
	printf("st_2.c_3    のアドレス = %p\n", &st_2.c_3);
	printf("st_2.c_4    のアドレス = %p\n", &st_2.c_4);
	printf("st_2.i      のアドレス = %p\n", &st_2.i);
	printf("st_2.double のアドレス = %p\n", &st_2.d);
	
	return 0;
}

以下は、上記のプログラムを実行しています。

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ arch
x86_64
***@ubuntu:~/***/test/c$ gcc -dumpversion
7
***@ubuntu:~/***/test/c$ gcc -dumpmachine
x86_64-linux-gnu
***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra sample.c -o sample
***@ubuntu:~/***/test/c$ ./sample
構造体 st_1 のサイズ --> 24
構造体 st_2 のサイズ --> 16

st_1.c_1    のアドレス = 0x7ffd77637090
st_1.i      のアドレス = 0x7ffd77637094
st_1.c_2    のアドレス = 0x7ffd77637098
st_1.c_3    のアドレス = 0x7ffd77637099
st_1.c_4    のアドレス = 0x7ffd7763709a
st_1.double のアドレス = 0x7ffd776370a0

st_2.c_1    のアドレス = 0x7ffd77637080
st_2.c_2    のアドレス = 0x7ffd77637081
st_2.c_3    のアドレス = 0x7ffd77637082
st_2.c_4    のアドレス = 0x7ffd77637083
st_2.i      のアドレス = 0x7ffd77637084
st_2.double のアドレス = 0x7ffd77637088

上記に示した通り、構造体 st_1 のサイズは 24 バイト、構造体 st_2 のサイズは 16 バイトとなりました。

構造体 st_1 と st_2 の構造体メンバは、以下の順序でメモリに配置されています。

構造体 st_1 のメモリ配置

struct ST_1 {
	char c_1;	// 1バイト
	int i;		// 4バイト
	char c_2;	// 1バイト
	char c_3;	// 1バイト
	char c_4;	// 1バイト
	double d;	// 8バイト
};
struct ST_1 st_1;

0:char c_1 (1 バイト)
1:パディング
2:パディング
3:パディング
4:int i (4 バイト)
5:int i
6:int i
7:int i
8:char c_2 (1 バイト)
9:char c_3 (1 バイト)
10:char c_4 (1 バイト)
11:パディング
12:パディング
13:パディング
14:パディング
15:パディング
16:double d (8 バイト)
17:double d
18:double d
19:double d
20:double d
21:double d
22:double d
23:double d

構造体 st_2 のメモリ配置

struct ST_2 {
	char c_1;	// 1バイト
	char c_2;	// 1バイト
	char c_3;	// 1バイト
	char c_4;	// 1バイト
	int i;		// 4バイト
	double d;	// 8バイト
};
struct ST_2 st_2;

0:char c_1 (1 バイト)
1:char c_2 (1 バイト)
2:char c_3 (1 バイト)
3:char c_4 (1 バイト)
4:int i (4 バイト)
5:int i
6:int i
7:int i
8:double d (8 バイト)
9:double d
10:double d
11:double d
12:double d
13:double d
14:double d
15:double d

タイトルとURLをコピーしました