メモリは番号の付いたバイトの列
C言語の変数は、メモリの中で決まった大きさと場所を持ちます。メモリは、番号の付いた1バイトずつの箱が、ずらりと並んだものです。
この番号を番地(アドレス)と呼びます。変数の大きさと番地が分かると、プログラムが何バイト使うかが読めます。構造体が、メンバーの合計より大きくなる理由も分かります。
このレッスンのゴール
メモ
変数の大きさと、メモリのどこにあるかを言える。
重要な言葉
| ことば | いみ | れい |
|---|---|---|
| バイト | メモリの箱1つ。0から255までの数が入る | |
| 番地 | バイトに付いた番号。アドレスとも言う | |
| 型 | 変数が何バイトで、どう読むかを決めるもの | int |
| アライメント | 変数を置ける番地の決まり。大きさの倍数の番地 | |
| パディング | アライメントのために入れる、使わない空きバイト | |
| 構造体 | いくつかの変数を1つにまとめたもの | struct |
学生が入力するセルは、このプレビューでは空白です。
型が変数のバイト数を決める
C言語では、型が変数のバイト数を決めます。char は1バイト、short は2バイト、int は4バイト、double は8バイトです。
sizeof という演算子は、型や変数の大きさをバイト数で返します。この数は、64ビットの Linux なら、どの機種でも同じです。
型と大きさの一覧
| 型 | バイト数 | 入れるもの |
|---|---|---|
| char | 1 | 1文字、小さな整数 |
| short | 2 | 小さめの整数 |
| int | 4 | ふつうの整数 |
| long | 8 | 大きな整数 |
| double | 8 | 小数 |
| ポインタ | 8 | 番地 |
学生が入力するセルは、このプレビューでは空白です。
4つの型の大きさ
printf '%s\n' 'int main(void){printf("%zu %zu %zu %zu\n", sizeof(char), sizeof(short), sizeof(int), sizeof(double)); return 0;}' | gcc -include stdio.h -x c - -o /tmp/sizes && /tmp/sizesここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
long とポインタの大きさ
printf '%s\n' 'int main(void){printf("%zu %zu\n", sizeof(long), sizeof(void *)); return 0;}' | gcc -include stdio.h -x c - -o /tmp/wide && /tmp/wideここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
変数は大きさの倍数の番地に置かれる
int は4バイトなので、4の倍数の番地に置かれます。0、4、8、12 の番地は使えますが、番地6は使えません。この決まりがアライメントです。
char のあとに int を置くと、間の3バイトが空きます。この空きがパディングです。メンバーの合計より、構造体が大きくなる理由です。
3つのメンバーを持つ構造体
struct a {
char x; /* 1バイト */
int y; /* 4バイト */
char z; /* 1バイト */
};12バイトの中身
| 番地 | 中身 | 使うバイト |
|---|---|---|
| 0 | x | 1 |
| 1から3 | パディング | 3 |
| 4から7 | y | 4 |
| 8 | z | 1 |
| 9から11 | パディング | 3 |
学生が入力するセルは、このプレビューでは空白です。
並べ方で大きさが変わる
printf '%s\n' 'struct a {char x; int y; char z;}; struct b {int y; char x; char z;}; int main(void){printf("%zu %zu %zu\n", sizeof(struct a), sizeof(struct b), offsetof(struct a, y)); return 0;}' | gcc -include stdio.h -include stddef.h -x c - -o /tmp/pad && /tmp/padここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
int の番地を4で割った余り
printf '%s\n' 'int main(void){int x = 1; printf("%d\n", (int)((size_t)&x % 4)); return 0;}' | gcc -include stdio.h -x c - -o /tmp/al && /tmp/alここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
構造体の大きさは、メンバーの大きさの合計だ、と思いがちです。上の例なら 1 + 4 + 1 で6バイトです。実際は、アライメントのためにパディングが入るので12バイトです。大きさを知りたいときは、いつも sizeof で測ります。
さらに挑戦: 配列の大きさ
課題が通ったら、同じ構造体を10個並べた配列の大きさを考えましょう。1つが12バイトなら、配列は120バイトです。
まず、struct_size を使う array_size(fields, count) を書きます。そして、メンバーを並べ替えると配列がいくつ小さくなるかを、説明しましょう。