C言語のsizeofにおける配列、ポインタ、構造体などの動きのまとめ

C言語のsizeofについて色々調べたのでまとめてみました。

sizeofとは何か?

sizeofとは、型や変数などのメモリサイズを返す関数のこと。例えば、intやfloat等の型を指定した場合は「型が利用するメモリサイズ」を返し、変数やポインタ、構造体を指定した場合は「変数等で確保されているメモリサイズ」を返す。

sizeofの戻り値の型はsize_tであり、32bitか64bitなのか等の環境の違いによってsize_tの型も変わる。(例えば、32bitであれunsigned intが使用されて、64bitであればunsigned long intが使用される。また、OSによる違いもある)

#include <stdio.h>
void main() {
printf("char size is %d\n", sizeof(char));
printf("char* size is %d\n", sizeof(char *));
printf("int size is %d\n", sizeof(int));
printf("int* size is %d\n", sizeof(int *));
printf("float size is %d\n", sizeof(float));
printf("float* size is %d\n", sizeof(float *));
printf("double size is %d\n", sizeof(double));
printf("double* size is %d\n", sizeof(double *));
}

実行すると、以下の様な結果になった。

char size is 1
char* size is 4
int size is 4
int* size is 4
float size is 4
float* size is 4
double size is 8
double* size is 4

参考記事:sizeof operator in C – GeeksforGeeks

参考記事:sizeof演算子

あと、最近叩かれている某プログラミングスクールの記事では以下の様なコードを書いていたが、sizeofの使い方としては全くダメな例だ。

#include <stdio.h>
void main() {
printf("char size is %d\n", sizeof(32));
}

上のコードでは、sizeof(32)で32byteが返ってくることを想定して書いたコードだろう。しかし、実際には32は単にint型の整数だとみなされて、sizeof(int)とした時と全く同じ結果が返ってくる。

むしろ、可読性が悪くなっているので、sizeof(int)よりもダメなコードと言える。

参考記事:侍エンジニア塾のC言語のサンプルがヤバすぎる。 – Qiita

sizeofのポインタについて

sizeofでポインタを指定する場合は、どの型や変数に関わらず4byteを返す。これはポインタが「アドレスを格納する」と言う役割を考えれば当然だと言える。

参考記事:ポインタ変数のサイズ

実際に、以下のコードを試してみる。

#include <stdio.h>
void main() {
printf("char* size is %d\n", sizeof(char *));
printf("int* size is %d\n", sizeof(int *));
printf("double* size is %d\n", sizeof(double *));
}

結果は以下の通り、全てのポインタが4byteになる。

char* size is 4
int* size is 4
double* size is 4

配列のsizeofについて

sizeofに配列を指定すると、配列名を指定した場合はその配列全体のメモリサイズ、配列の1つの要素に対してsizeofを指定するとその要素のメモリサイズが表示される。

#include <stdio.h>
void main() {
double array_d[50];
printf("array_d is %d byte.\n", sizeof(array_d));
printf("array_d[10] is %d byte.\n", sizeof(array_d[10]));
}

結果は以下の通り。

array_d is 400 byte.
array_d[10] is 8 byte.

先ほどの例では配列の要素名を指定した上でsizeofでメモリサイズを測定したが、配列の要素数が不確定の場合の方がsizeofを使うメリットが上がると思う。

#include <stdio.h>
void main() {
double array_d[] = {4, 5, 55466, 43.55, 5552};
printf("array_d is %d byte.\n", sizeof(array_d));
printf("array_d[10] is %d byte.\n", sizeof(array_d[10]));
}

結果は以下の通りになった。

array_d is 40 byte.
array_d[10] is 8 byte.

構造体のsizeofについて

構造体のsizeofはその構造体の合計メモリサイズ以上の値を返す。必ずしも、その構造体の合計メモリサイズぴったりの値が返るわけではない。

例えば、以下のコードを書いたとする。

#include <stdio.h>
typedef struct {
int n;
int array_n[10];
double dn;
} TestStruct;
void main() {
TestStruct test;
printf("test.n: %d, test.array_n: %d, test.dn: %d, test: %d",
sizeof(test.n), sizeof(test.array_n), sizeof(test.dn), sizeof(test));
}

上のコードの構造体はint型が1つ、int型の要素10つの配列が1つ、double型が1つで構成されているため合計52byteが返ってくると期待するだろう。
しかし、期待と異なる結果を返す。

test.n: 4, test.array_n: 40, test.dn: 8, test: 56

構造体の合計は本来52byteであるはずなのに、結果は56byteと返ってきた。

これはコンパイラが構造体を作る時に、各メンバをびっちり詰めて構造体を作るのではなく、各メンバと構造体の終わりに1byteの隙間を入れて構造体を作成するからであり、コンパイラの仕様とも言える動きだ。

参考記事:size of struct in C – Stack Overflow

ただ、絶対に隙間を作るわけではなく、例えば構造体のメンバがintとcharだけ等の場合は隙間を作らない場合もある。

ReratedPosts

Linuxのopen(低水準入出力)の使い方やfopenとの違いをまとめていく
ダブルポインタ(ポインタのポインタ)のメリットや使い道を紹介する
segmentation fault 11(core dumped)の原因と2つのチェックポイント
C言語でTCPクライアント・サーバーを実装する
C言語のfopen関数で「警告: 2 番目の ‘fopen’ の引数へ渡すときに整数からキャスト無しにポインタを作成しています [デフォルトで有効]」と言うエラーが出たときの対処法
C言語のvoidの使い方について