答え
構造体のサイズを、メンバーのサイズの合計だと考えているのが間違い!
AlphabetInfo *info = malloc(5);
こう書いても結果は同じなので、やっぱり間違い!
AlphabetInfo *info = malloc(sizeof(uint8_t) + sizeof(uint32_t));
構造体のサイズは全体で表すのが正解!
AlphabetInfo *info = malloc(sizeof(AlphabetInfo));
解説
これは、「アラインメント」に関する出題でした。アラインメントとは、メモリー上にデータを置くときに都合がいい「区切り」のことです。
例えばuint32_t
のサイズは4バイトですね。多くのコンピューターでは、4バイトのデータは4の倍数のアドレスに配置されます。そうすることによって、データを高速で読み書きできるようになっているのです。
構造体のメンバーについても、話は同じです。今回の構造体は、こういう形をしていました。
typedef struct {
uint8_t letter;
uint32_t order;
} AlphabetInfo;
uint8_t
のサイズは1バイトですね。その直後に4バイトのuint32_t
を置くのは都合が悪いので、メモリー上ではletter
のあとは3バイトとばしてorder
が配置されます。ちなみに、このような3バイト分のメモリーは、間を埋めているだけなので「パディング」と呼ばれます。
そういうわけで、構造体のサイズは、すべてのメンバーのサイズを合計した値と一致するとは限りません。この現象は、次のようにすれば確かめられるでしょう。
サンプルコード
printf("total size = %zu\n", sizeof(uint8_t) + sizeof(uint32_t));
printf("struct size = %zu\n", sizeof(AlphabetInfo));
実行結果
total size = 5
struct size = 8
うわぁ、ほんとに3バイト増えてますよ!
でしょ。つまりね、構造体のサイズを知りたかったら、素直に
sizeof(構造体名)
とすればいいのよ。
そっか。そう言われると単純な答えですね。
じゃあ、構造体のメンバーの並び順を入れ替えたらどうなると思う?
えっ?
並び順を入れ替えると
uint8_t
、つまり1バイトのメンバーがうしろにくるから……
typedef struct {
uint32_t order;
uint8_t letter;
} AlphabetInfo;
パディングは必要ないんじゃないかな。確かめてみよう!
printf("total size = %zu\n", sizeof(uint32_t) + sizeof(uint8_t));
printf("struct size = %zu\n", sizeof(AlphabetInfo));
実行結果
total size = 5
struct size = 8
ええっ!入れ替えてもサイズが変わらないじゃないですか!
そうなのよ。構造体の最後に、やっぱり3バイトのパディングが入っているわね。なんでか分かる?
うーん、メモリーを無駄遣いしているようにしか思えないんですけど……
これはね、配列にしたときのことまで考えられているからなの。
構造体の配列ってことですか?
そう。配列って、要素が隙間なく並べられるでしょう?だから、もし最後にパディングがなかったら……
あ、もしかして……。すぐ隣の要素のアラインメントがずれちゃうってことですか?
そういうこと!
なるほどー!うまく考えられてるんですね!
ここがポイント!
メモリー上のデータは、サイズによって都合のいい「区切り」で配置されていることを覚えておこう!
修正後のプログラム
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
typedef struct {
uint8_t letter;
uint32_t order;
} AlphabetInfo;
AlphabetInfo *mallocAlphabetInfo(uint8_t letter) {
const char *pAll = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char *pFound = strchr(pAll, toupper(letter));
if (pFound) {
AlphabetInfo *info = malloc(sizeof(AlphabetInfo));
if (info) {
info->letter = letter;
info->order = 1 + (uint32_t)(pFound - pAll);
return info;
}
}
return 0;
}
int main(void) {
AlphabetInfo *info = mallocAlphabetInfo('x');
if (info) {
printf("'%c' is the %dth letter.\n", info->letter, info->order);
free(info);
info = 0;
}
return EXIT_SUCCESS;
}
実行結果
'x' is the 24th letter.