sizeof演算子について検索してくる人が多いようなので,真面目にsizeof演算子についても書いておくことにします.
簡単に言うと,sizeofに渡された型や変数のメモリサイズを調べるものです.
sizeof演算子は2種類の使い方があります.
sizeofに変数を渡した場合,その変数名で確保されているメモリサイズを返します.
int data[50];
int size = sizeof(data); // dataは配列全体
int *data;
int size = sizeof(data); // dataはint型のポインタ
int *data;
data = (int *)malloc(sizeof(int) * 100);
int size = sizeof(data); // いったい何を指すのか?
#include <malloc.h>
char *data;
data = (char *)malloc(100);
int size = malloc_usable_size(data); // dataはmallocによって返されたポインタ
いくつかのsizeofに関しての例をあげておきます
sizeofに定数を渡すことができることから,文字列に必要なメモリの長さを知ることができます.
#define STR "文字列"
int size = sizeof(STR); // defineで定義された文字列に必要なメモリの長さ
実際には,終端文字の都合上size+1のメモリが必要です.
char *STR = "文字列"
int size = sizeof(STR); // ここでSTRはchar型のポインタなのでポインタのサイズになる
もちろんsizeofの結果をsizeofに渡すこともできます
int size = sizeof (sizeof(int)); // sizeofの返値を引数としてsizeofを行う
sizeofの使い道の一つとしてforループのループ数の指定にあります.
double data[50]; double sum = 0; for(int i = 0; i < sizeof(data) / sizeof(data[0]); ++i){ sum += data[i]; }
ここで,sizeof(data)は,data[50]全体を指しますので,8 (double型のサイズ) x 50 = 400 となります.
次に,sizeof(data[0])は,一つの要素を指しますので,8 (double型のサイズ) となります.
そのため,400 / 8 = 50となり,dataの持つ要素数となります.
この方法で書いておくと,dataの型を変更した場合や,要素の数を変更した場合でもforの条件を書き換えずに,
要素全てに対して処理をおこなうことができます.
double *data; double sum = 0; data = (double*)malloc(sizeof(double) * N); for(int i = 0; i < sizeof(data) / sizeof(data[0]); ++i){ sum += data[i]; } free(data);
ここで,sizeof(data)は,data全体ではなく,dataの型つまりdouble *のサイズを示してしまいます. そのため,前の例のように,Nの回数分だけforの処理をすることはできません.
sizeof演算子は、変数(型)のサイズを知るためによく使われますが、実はsizeof演算子を用いなくても、 サイズを知ることはできます。
この実行結果は、(処理系にもよりますが)4になるはずです。
プログラム1 1:int ans;
2:ans = (int)&(((int*)0)[1]);
また、構造体の途中までのサイズを知ることもできます。
この実行結果は、12になるはずです。
プログラム2 1:struct _data{
2: int A;
3: double B;
4: char C[4];
5:};
6:
7:int ans;
8:ans = (int)&(((struct _data*)0)->C);
さて、なぜこのような結果になるか考えてみましょう。
さきほどの、(int)&(((struct _data*)0)->C)を見てみます。
これは、次の順に解釈されます。
同様にして、(int)&(((struct _data*)0)[1])は、配列の次のアドレスまで、 つまり、一つの構造体のサイズを指すことになります。
1:(struct _data*)0 0をポインタと見なして、struct _data型にキャスティングする。
2:((struct _data*)0)->C キャスティングした結果をもとに、変数Cを参照する。
3:&(((struct _data*)0)->C) 参照したアドレスを読み出す。
4: この時点で、0を基点としたCまでの距離が求められる。
5:(int)&(((struct _data*)0)->C) 読み出したアドレスをint型にキャスティングする。
ただし、これは構造体がきれいである必要があります。
プログラム中で使われる変数の開始位置は、メモリアクセスの関係上、通常は4バイト境界に置かれます。
そのため、構造体中にchar C[5];といったように4の倍数以外のサイズが紛れ込んだとき、
次の4バイト境界が出るまで未使用のメモリブロックとし、空白のメモリができてしまいます。
プログラム2において、char C[5];とすると、(int)&(((struct _data*)0)[1])の値は20となり、
実際の17とは異なる値となってしまいます。
追記
GNU libcは,8バイトになっているようですね.