こんにちは、ナナです。
sizeof演算子は変数のメモリサイズを算出するための演算子です。メモリのサイズを算出することで、便利なシーンというものがあります。基本的な利用方法と実践的な利用方法を紹介します。
本記事では次の悩みを解消する内容となっています。
では、sizeof演算子を学んでいきましょう。
sizeof演算子の役割と書き方
sizeof演算子は変数やデータ型の使用メモリサイズを算出する際に使用します。
sizeof演算子の書き方
sizeof演算子は次のように書きます。
書き方
sizeof(データ型)
sizeof(変数名)
使用例
sizeof(long)
sizeof(num)
sizeof演算子は()が必ずしも必要ではないのですが、算出対象を明確にするために()で括るのが一般的な使用方法なんです。
sizeof演算子を利用したサンプルコードと結果
sizeof演算子を実際に利用したサンプルコードを示します。num1~num6の変数を異なるデータ型で定義しました。
#include <stdio.h>
int main(void)
{
char num1;
short num2;
long num3;
int num4;
float num5;
double num6;
// データ型と変数名のsizeof結果を表示
printf("char :%d num1:%d\n", sizeof(char), sizeof(num1));
printf("short :%d num2:%d\n", sizeof(short), sizeof(num2));
printf("long :%d num3:%d\n", sizeof(long), sizeof(num3));
printf("int :%d num4:%d\n", sizeof(int), sizeof(num4));
printf("float :%d num5:%d\n", sizeof(float), sizeof(num5));
printf("double:%d num6:%d\n", sizeof(double), sizeof(num6));
return 0;
}
表示結果は次のものとなります。データ型でも変数名でも同一のメモリサイズが取得できていることがわかりますね。
char :1 num1:1
short :2 num2:2
long :4 num3:4
int :4 num4:4
float :4 num5:4
double:8 num6:8
サイズを知ることで何ができるかは一旦置いておきましょう。まずは、このような方法でメモリサイズを取得できることを覚えましょう。
なぜ、データ型でも変数名でもサイズが算出できるのか?
sizeof演算子として指定できるものは「データ型」「変数名」のどちらかです。なぜ、どちらでもよいのかを解説しておきましょう。
「変数」というのは「データ型」を元にメモリ上に作成されます。イメージとしては次のような「たい焼きの鉄板」という型から「たい焼き」という変数を作り出すといったものです。
では、ここで皆さんに質問です。たい焼きのサイズが何cmなのかを調べてください。
この質問には2つの調べ方がありますね。
- 鉄板の型からたい焼きのサイズを調べる
- 実際に焼いたたい焼きからサイズを調べる
つまり、「型」だろうと「型から作られたもの」であろうと、どちらからでもサイズを算出することができますね。
sizeof演算子で「データ型」でも「変数」からでもメモリサイズの算出ができるのは、このたい焼きのサイズを求める原理と同じなのです。
sizeofを使った特殊なメモリサイズ算出
sizeofで算出できるメモリサイズはchar型やlong型といった組み込み型だけではありません。その他にも利用できるものがありますので紹介しましょう。
構造体のメモリサイズを求める
構造体という型もデータ型のひとつであり、例外ではありません。皆さんが作成した構造体もサイズを算出することが可能です。
#include <stdio.h>
// 構造体のデータ型定義
typedef struct
{
short num1;
long num2;
} S_NUM;
int main(void)
{
S_NUM tmp; // 構造体変数の定義
// 構造体のデータ型と変数のサイズ表示
printf("S_NUM :%d tmp :%d \n", sizeof(S_NUM), sizeof(tmp));
return 0;
}
S_NUM :8 tmp :8
構造体のメモリサイズはパディングの影響で、構造体メンバの総サイズと異なることがあります。そのため、正確な構造体サイズを算出する際には、必ずsizeof演算子によって求めます。
パディングや構造体について知りたい方はこちらの記事を見てください。
》参考:構造体 struct【情報のパッケージ化とそのメリット】
構造体のメモリサイズが欲しいときって実はあるんです。後ほど解説しましょう。
配列のトータルサイズを求める
配列とは同一データ型の変数が複数連続で並んだものですね。
配列に対してのsizeofを使用すると、配列全体の総メモリサイズが算出できます。
書き方
sizeof(配列変数名)
使用例
char num[5];
sizeof(num)
イメージ図で示すと、次のようにサイズが算出されているということになります。
実際のプログラムでの使用例は次のものになります。
#include <stdio.h>
int main(void)
{
char num1[10];
short num2[10];
long num3[10];
// 配列名をsizeofに指定
printf("num1 :%d num1[0] :%d\n", sizeof(num1), sizeof(num1[0]));
printf("num2 :%d num2[0] :%d\n", sizeof(num2), sizeof(num2[0]));
printf("num3 :%d num3[0] :%d\n", sizeof(num3), sizeof(num3[0]));
return 0;
}
配列変数名と配列要素を指定した場合で取得できるサイズが異なることに着目しましょう。
配列要素を指定した場合は、要素1つ分のメモリサイズが取得できます。
num1 :10 num1[0] :1
num2 :20 num2[0] :2
num3 :40 num3[0] :4
ポインタ変数に対するsizeof演算子
ポインタに対してsizeof演算子を利用する場合は注意が必要です。ポインタに対してどのメモリサイズを算出しようとしているのかを正確に把握する必要があります。
#include <stdio.h>
int main(void)
{
char num1 = 100;
char * pnum1 = &num1;
printf("char* :%d \n", sizeof(char*));
printf(" pnum1 :%d \n", sizeof(pnum1));
printf("*pnum1 :%d \n", sizeof(*pnum1));
return 0;
}
char* :4
pnum :4
*pnum :1
ポインタには3パターンの指定方法があります。次の関係性を把握した上で利用しましょう。
ポインタに対するsizeof演算子は記述のちょっとした違いで算出されるメモリサイズが変化します。しっかりと把握しましょう。
sizeof演算子を使った実践的な活用例
実践でsizeof演算子をよく利用するシーンがあります。
配列要素数の算出に利用する
配列を定義する際に、初期値を与えることで配列要素数の指定を省略することが可能です。
配列要素数を省略できることは、メンテナンスの面を考えるとメリットがあります。しかし、次のように配列はループ処理をする際に、どうしても配列要素数が必要となります。
#include <stdio.h>
int main(void)
{
// 初期値を与え配列要素数を省略
long num[] = { 100, 200, 300, 400, 500 };
int i;
// 配列要素数の5を指定する必要がある
for (i = 0 ;i < 5 ; i++)
{
printf("%d\n", num[i]);
}
return 0;
}
せっかく配列定義で要素数を省略したのに、これでは要素数を省略したメリットが半減してしまいますね。
ここで出てくるのがsizeofを利用した配列要素数の算出ロジックです。
配列要素数の算出
sizeof(配列変数名)/ sizeof(配列要素)
先ほどのプログラムは次のように書き直すことができます。
#include <stdio.h>
int main(void)
{
// 初期値を与え配列要素数を省略
long num[] = { 100, 200, 300, 400, 500 };
int i;
// 配列要素数をsizeofで算出
for (i = 0 ;i < sizeof(num)/sizeof(num[0]) ; i++)
{
printf("%d\n", num[i]);
}
return 0;
}
これで配列要素数の「5」という数字を除去することができるようになりました。
配列要素数を求めるこのテクニックは非常によく利用されます。
mallocによるヒープメモリの確保
mallocを利用したヒープメモリの取得においてもsizeofをよく利用します。malloc関数は引数にて確保したいメモリサイズを指定する必要があります。
よくあるのが、構造体を配列としてヒープメモリを確保したいといったケースです。このパターンは実践ではよく利用されます。
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
short num1;
long num2;
} S_NUM;
int main(void)
{
S_NUM * pnum;
int i;
// 構造体を100個分のヒープメモリを確保
pnum = (S_NUM *)malloc(sizeof(S_NUM) * 100);
// 確保した構造体メモリにデータを設定
for (i=0 ; i < 100 ; i++)
{
pnum[i].num1 = 10;
pnum[i].num2 = 50;
}
// 使い終わったらメモリを解放
free(pnum);
return 0;
}
malloc関数を利用し配列メモリを確保する場合は、「データ型のサイズ × 配列要素数」として指定します。データ型のサイズにはsizeofを利用するのです。
malloc関数を利用する際には、sizeof演算子がよく利用されます。