sizeof
主にCとC++において、sizeof
は、データ型の大きさを求める単項の演算子である。sizeof
は原則としてコンパイル時計算される演算子で、式もしくは括弧でくくった型指定子を与えるとその大きさをバイト単位で返す。これは組み込みの数値型(整数型や浮動小数点数型)、列挙型、ポインタ型、利用者定義の複合データ型(構造体、共用体、C++のクラス)まで、ほぼ全てのデータ型に対して使用できる。
必要性
[編集]多くのプログラムで、データ型の大きさがわかると便利な状況がある。最もよくある例としては標準Cライブラリのmalloc
などによる動的メモリ確保が挙げられる。組込型の大きさは処理系定義となっており、厳密な大きさはsizeof (char)
が1
であることを除いて標準に定められていない。次の例では10個の要素を持つint
型の配列を格納するのに十分なメモリを確保しようとしている。(処理系に依存したコードを書くつもりでなければ)int
型の正確な大きさはわからないのでsizeof
が必要となる。
int *pointer; /* intへのポインタ型、メモリ確保したデータを参照する */
pointer = malloc(sizeof (int) * 10);
このコードで、malloc
は確保したメモリ領域へのポインタを返すが、その大きさはちょうどint
型10個分になる。
一般的に、CとC++で型の大きさを仮定するのは安全でない。標準規格でもchar
以外の型がメモリ上で何バイト占めるかを規定していない。たとえば、16ビットシステムでのint
型の大きさは通例2バイトであるが、大半の32ビットシステムではint
型の大きさは4バイトである。また、構造体や共用体ではアライメントもあり、ますます正確な大きさを求めるのは困難となる。そのようなこともあり、移植性の高いプログラムを書くには型の大きさを求めるためにsizeof
を使用することが推奨されている。
CとC++
[編集]使用方法
[編集]sizeof
演算子は、メモリ上に領域を占めるものであれば、ほとんどどんなものに対しても使用できる。sizeof
を使うには、キーワードsizeof
の後に変数や式あるいは括弧でくくった上で型名を書く。変数や式の場合は、括弧でくくるかどうかは自由である。次の例ではint
型がchar
型の4倍のサイズを持つ実装の場合、1, 4
と出力される(なお、char
型にsizeof
演算子を適用した結果は全ての実装において1
でなければならない)。
// C99
char c;
printf("%zu, %zu", sizeof c, sizeof (int));
// Microsoft Visual C++
char c;
printf("%Iu, %Iu", sizeof c, sizeof (int));
// キャスト
char c;
printf("%lu, %lu", (unsigned long) sizeof c, (unsigned long) sizeof (int));
sizeof
の結果は実装定義の符号無し整数型であるsize_t
型となり、負数になることはない。
sizeof
を関数型、ビットフィールド、不完全型(後述)に対して使用することはできない。
なお、printf
などでsize_t
型を書式出力する場合、C99で追加された長さ修飾子z
を用いる。
C99に対応していない処理系では、引数をキャストするか、処理系依存の修飾子(たとえばMicrosoft Visual C++ならば修飾子I
)を用いる。
もし%u
を使用すると、size_t
型とunsigned int
型のサイズが異なる環境において未定義動作となる。
配列に対するsizeof
[編集]sizeof
が配列型に使用されると、その配列がメモリ上に占める大きさが演算結果となる。配列の大きさは、要素の型の大きさに要素数をかけた値と規定されており、例えば要素型T
についてsizeof (T[8])
はsizeof (T) * 8
と同じ値になる。逆に、配列x
に対して、sizeof x / sizeof x[0]
の演算により、配列の要素数を求めることが可能である。
次の例では、文字の複写時にバッファオーバーランを起こさないよう、sizeof
を配列の大きさを求めるために用いている。
/* sizeofを配列に使用する例 */
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer[10]; /* 要素数10のchar配列 */
/* 標準入力から読み取った結果をbufferへ9文字までのみ複写する。
*(sizeof (char)は常に1なので、sizeof buffer[0]で割る必要はない)
*/
fgets(buffer, sizeof buffer, stdin);
puts(buffer);
return 0;
}
C99で可変長配列に対して sizeof
を適用する場合、配列の大きさは実行時に動的に計算され、コンパイル時定数にはならない。
sizeofと不完全型
[編集]sizeof
は完全に定義されたデータ型(メモリレイアウトが確定した型)のみに適用できる。配列なら、要素数が変数宣言に含まれていなければならず、構造体や共用体ならメンバが完全に定義されていなければならない。例えば次の二つのソースファイルがあったとする。
/* file1.c */
int arr[10];
struct x { int one; int two; };
/* file2.c */
extern int arr[];
struct x;
どちらのファイルも正しいCのソースであるものの、"file1.c"ではsizeof
をarr
とstruct x
に使用できるが、"file2.c"では完全型でないため使用できない。"file2.c"ではarr
の要素数がわからず、struct x
のメンバも分からないためである。"file2.c"でarr
とstruct x
をsizeof
で使用できるようにするには、"file2.c"のarr
の宣言に要素数を指定したり、struct x
の完全な宣言を書いたりする必要がある。
構造体メンバのsizeof
[編集]構造体やクラスの非静的メンバに対してsizeof
を適用する場合、CおよびC++03規格までのC++では、構造体やクラスのオブジェクト(インスタンス)からメンバにアクセスする式に対して適用しなければならない。
#include <stdio.h>
struct my_type {
int member1;
short member2;
};
int main(void) {
struct my_type obj;
printf("%u\n", (unsigned)sizeof(obj.member1));
printf("%u\n", (unsigned)sizeof(obj.member2));
/* sizeofはコンパイル時に評価されるので、以下のように書いても実行時のNULLデリファレンスは発生しない */
printf("%u\n", (unsigned)sizeof(((struct my_type*)NULL)->member1));
printf("%u\n", (unsigned)sizeof(((struct my_type*)NULL)->member2));
return 0;
}
C++11規格以降では、スコープ解決演算子::
を利用して以下のように書けるようになった[1]。
#include <cstdio>
struct my_type {
int member1;
short member2;
};
int main() {
printf("%zu\n", sizeof(my_type::member1));
printf("%zu\n", sizeof(my_type::member2));
}
GCCなど、一部のコンパイラでは、C++03以前でもこの記法を拡張としてサポートしていた。
実装
[編集]コンパイラは言語の実装に適合するように、sizeof
演算子をデータ型のメモリ上に占める大きさを結果とするように実装しなければならない。また(既述の例外を除いて)これはコンパイル時計算される演算子であり、アセンブリ言語上では単なる即値になる。
構造体のパディング
[編集]利用者定義型の大きさはアライメントのためにメンバの大きさの合計よりも大きくなることが規格では許されている。次のコードは多くの環境において8
と出力される。
struct student {
char grade; /* charは1バイト */
int age /* intは4バイト */
};
printf("%zu", sizeof (struct student));
この理由は、多くのコンパイラでは通常ワード単位にデータを揃えるためであり、個々のメンバも境界を揃えられる。上の場合は、境界調整によってメンバ変数のage
が次のワード単位に置かれる。このような構造体には境界調整のためにメンバ間やメンバの後ろに「パディング」と呼ばれる余分な隙間が置かれる。多くのCPUではデータがワード単位のメモリアドレスに置かれていた方が高速に読み書きでき、また中にはワード単位に揃えられていないと読み書きできないCPUもある[2]。
D
[編集]D言語では全ての型が持っているプロパティとしてsizeof
が用意されている。
void main()
{
writefln(int.sizeof);
}
.NET
[編集].NET Framework/.NET Coreでは、Marshal.SizeOf
メソッド[3]によってオブジェクトのアンマネージサイズやアンマネージ型のサイズをバイト単位で取得可能である。.NET 4.5.1でジェネリックバージョンのオーバーロードが追加された。そのほか、各.NET言語に類似の組み込み言語機能が用意されていることがあるが、System.Boolean
型に対する演算結果など、必ずしもMarshal.SizeOf
と同じ結果になるとは限らない。
C++/CLI
[編集]C++/CLIのsizeof
演算子はネイティブ型(基本型およびポインタ型)に用いる限りコンパイル時定数となり、基本的にC++と同じである。しかし値クラス (value class
) 型、(マネージ)ハンドル型やジェネリック型引数に対して用いられたときにはコンパイル時定数でなくなる。また参照クラス (ref class
) 型やインターフェイス型に対して用いることはできない(不正となる)[4]。
C#
[編集]C#のsizeof
演算子は、アンマネージ型(組み込み型、列挙型、ポインタ型、参照型のフィールドやプロパティを含まないユーザー定義の構造体)のサイズをバイト単位で取得する。結果はint
型となる。特定の組み込み型に対してsizeof
演算子を用いた場合の結果はコンパイル時定数となるが、それ以外の型に使用した場合はコンパイル時定数とならない。また、sizeof
演算子を使用するにはunsafeモードが必要だが、C# 2.0以降は組み込み型に対するsizeof
に関してのみunsafeが不要となった[5]。
Visual Basic
[編集]Visual Basicでは、Len
関数などが存在する。Len
は文字列を引数に与えると文字列の長さを返すが、その他の型の変数を与えると変数の大きさを返す。
ActiveBasic
[編集]ActiveBasicでは、Visual Basicと同様のLen
組込関数を持っているほか、SizeOf
組込関数を持っている。Len
は型名を指定することができないが、SizeOf
は型名を指定できる。逆にSizeOf
に変数や式を指定することはできない。
脚注
[編集]- ^ sizeof演算子にクラスの非静的メンバを、オブジェクトを作らずに指定できるようにする - cpprefjp C++日本語リファレンス
- ^ Rentzsch, Jonathan. "Data alignment: Straighten up and fly right."[リンク切れ] www.ibm.com. 08 FEB 2005. Accessed 1 Oct 2006
- ^ Marshal.SizeOf Method (System.Runtime.InteropServices) | Microsoft Docs
- ^ Standard ECMA-372
- ^ sizeof (C# Reference) | Microsoft Docs