第20回
いろいろな演算子~演算子の優先順位

変数のサイズを知る~sizeof演算子

四則演算子や論理演算子はほとんどすべてのプログラミング言語に備わっています。が、Cにはsizeofという『データの占有するメモリのサイズを調べる』という、ちょっと変わった演算子があります。

占有するメモリをバイト数で返す

書式は以下のようになります。
sizeof(<型名>)
<型名>で示す型がメモリ上で占有するサイズをバイト数で返します。戻り値はint型です。

例えば
unsigned int n;
n = sizeof(char);
とすれば、変数nにはchar型の値が占有するメモリサイズとして1が代入されます。

n = sizeof(long);
とすれば、変数nにはlong型(long int型)の値が占有するメモリサイズとして4が代入されます。int型は処理系が最も効率的に処理できる整数型とされるため、sizeof(int);とした場合には処理系によって2(short int)あるいは4(long int)など、異なる値が返ってきます。

配列のサイズを調べる

引数に配列名を与えると、その配列が占有している全領域のサイズが返ってきます。

char str[16];
n = sizeof(str);
とすれば、変数nにはchar型配列strのサイズとして16が代入されます。予め要素数を宣言した変数では特に意味はありませんが、宣言時に文字列定数を代入した場合に、そのサイズを知ることができます。

char str[] = "Hello!";
n = sizeof(str);
とすれば、変数nには文字列"Hello!"を格納するために必要なサイズとして7(6文字+'\0')が代入されます。

メモリを確保する関数と併用

多くのプログラミング言語では、ある変数がメモリをどれだけ占有しているかを知る必要はありません。変数に対するメモリの割り当て処理は、処理系が行ってくれるからです。

Cには、メモリを動的に確保する機能があります。代表的なものはmalloc関数です。書式は以下のようになります。
malloc(<サイズ>)
<サイズ>にバイト数を与えるとそのサイズ分のメモリを確保し、先頭のアドレスをvoid型のポインタとして返します。voidは型のない型とよく言われますが、それはどのような型にでも変換して使用できる──ということです。

longやdoubleなど一般的な型の変数は、それを宣言したときにメモリが確保されるので問題はありません。しかし、構造体と呼ばれる複数の変数を包括した型を定義した場合、そのサイズは定義によって異なります。

構造体の概要

構造体については、追って詳しく説明することにします。ここでは、その概略だけ紹介しておきましょう。

例えば、商品の名前と単価を扱う場合に、それぞれを別の変数とすると分かりにくいため、Cでは以下のように構造体を定義できます。
typedef struct {
  char name[256]; ---------------- char型配列(文字列)
  unsigned short int value; ------ short int型
} _item; ------------------------- 構造体名
これで、char型配列とunsigned short int型の2つの値を包含する"_item"という新たな型が定義されます。nameとvalueは構造体を構成する要素で「メンバ(member)」と呼ばれます。

_item tool;
とすれば、toolという名前の_item型の変数が宣言できます。変数を1つだけ、あるいは
_item tools[16];
のように要素数の固定された配列として宣言すれば、そのためのメモリは宣言時に確保されます。

構造体をつなぐ

構造体型の変数が必要になるたびに、1つずつメモリを確保していくことも可能です。そのためには、構造体を以下のように定義します。
typedef struct _Item{
  char name[256];
  unsigned short int value;
  struct _Item * next; -------- 自分自身の型のポインタ
} _item;
上記の宣言では、構造体の最後のメンバに自分自身(struct _Item)の型のポインタを置いています。これによって1つずつ確保した構造体を次々と指し示していけるようになります。これを(構造体の)リンクと呼びます。ポインタによって構造体をつないだ形です。


1つずつメモリを確保していく

このような場合に、
_item *p;
p = (_item *)malloc(sizeof(_item));
とすると、char型配列とunsigned short int型の変数、そして自分自身を示すポインタの3つのメンバを持った構造体"_item"のためのメモリ領域が確保され、その先頭アドレスが変数pに代入されます。

配列、構造体、ポインタといった言葉については、まだまだ説明していないことが多いため、分かりにくいかもしれません。ここでは、sizeof演算子が構造体という複雑な型の変数に必要なメモリを動的に確保(生成)するために役立つ――ということだけを覚えておいてください。実際にどのような形で構造体を扱うのかについては、回を追って詳しく説明します。

構造体のリンクにもいくつかの形がありますが、これについてもアルゴリズムの解説で詳しく紹介することにします。

C++やC#にも

なお、sizeof演算子は、CをベースにしたC++やC#にも備わっています。また、Visual BasicではLEN関数が似たような機能を持っています。LEN関数は、引数に文字列名を与えるとその長さを返しますが、文字列以外の型の変数名を与えると、その変数の占有するメモリのサイズを返します。

Visual Basicにも構造体(バージョン6以前では「ユーザー定義型」)の機能があり、複数の変数を包含した新たな型を定義できます。その変数名をLEN関数に与えれば、構造体のサイズを取得できます。