久しぶりにc言語を触っていて
そういえば構造体の仕組みをあんまり理解していなかったなと思い、
改めて調べてみました。
構造体とは?
構造体は、異なる型のデータを1つにまとめて扱うための「設計図」です。
例えば、以下のように定義できます。
typedef struct s_student{
int id;
char sex;
int age;
char name[20];
double height;
double weight;
}t_student;
この時点では、メモリ上に実体はありません。
あくまでも設計図だからです。
構造体変数を定義すると、初めてアドレスが割り当てられます。
int main(void){
t_student student;
printf("構造体の先頭アドレス : %p\n", &student);
}
実行結果 (例)
$> 構造体の先頭アドレス : 0x7fffc3d92390
構造体変数を定義したらアドレスが割り当てられることは分かったけど
いったいどの範囲までメモリ領域が確保されているんだろうって思いましたよね?
メンバ変数のアドレスを見てみましょう!
printf("構造体の先頭アドレス : %p\n", &person);
printf("idのアドレス : %p\n", &person.id);
printf("sexのアドレス : %p\n", &person.sex);
printf("ageのアドレス : %p\n", &person.age);
...
出力結果 (例)
$>
構造体の開始アドレス: 0x7ffcdbe58b70
idのアドレス : 0x7ffcdbe58b70
sexのアドレス : 0x7ffcdbe58b74
ageのアドレス : 0x7ffcdbe58b78
ふむふむ、なるほど...ん?
sexのアドレスとageのアドレスが4バイトずれてる...
メンバ変数sexはchar型で1バイトだからageは0x7ffcdbe58b75
から始まるべきでは?
なぜ4バイト
ずれてしまったのか
アライメント(整列)のためです
構造体の中では、パディング(隙間)を入れることでアライメントを保っています。
構造体の開始アドレス: 0x7ffcdbe58b70
[0x7ffcdbe58b70 - 0x7ffcdbe58b73] → int id (4バイト)
[0x7ffcdbe58b74] → char sex (1バイト)
[0x7ffcdbe58b75 - 0x7ffcdbe58b77] パディング (無視される隙間)
[0x7ffcdbe58b78 - 0x7ffcdbe587b] → int age (4バイト)
なぜアライメントを保つ必要があるのか
パソコンのCPUがデータアクセスを効率的に行うためです。
現代のパソコンは、メモリを「ワードサイズ」で読み書きを行います。
32ビットシステムなら4バイトずつ、64ビットシステムなら8バイトずつ処理されます。データがアライメントされていないと、CPUは複数回メモリへアクセスする可能性がありパフォーマンスが低下します
そのため、各データ型はバイトのサイズに合わせたアライメントを持ちます。
-
char
: 1バイト境界(どこでも配置可能) -
short
: 2バイト境界(2の倍数のアドレスに配置可能) -
int
,float
: 4バイト境界(4の倍数のアドレスに配置可能) -
double
: 8バイト境界(8の倍数のアドレスに配置可能)
おわりに
構造体の仕組みを深掘っていくと
メモリの配置にきちんとしたルールや工夫があることが分かりました!
これからも精進していきます