sizeof

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

主にCC++において、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でなければならない)。

char c;
printf("%zu, %zu", sizeof c, sizeof (int));

sizeofの結果は実装定義の符号無し整数型であるsize_t型となり、負数になることはない。

sizeofを関数型、ビットフィールド、不完全型(後述)に対して使用することは不可能である。

なお、printfなどでsize_t型を出力する場合、C99で追加された長さ修飾子zを用いる。 C99に対応していない処理系では、引数をキャストするか、処理系依存の修飾子を用いる。 %uを使用すると、size_t型とunsigned int型のサイズが異なる環境において正常に動作しない。

配列に対するsizeof[編集]

sizeofが配列型に使用されると、その配列がメモリ上に占める大きさが演算結果となる。配列の大きさは、要素の型の大きさに要素数をかけた値と規定されており、例えば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は完全に定義されたデータ型のみに適用できる。配列なら、要素数が宣言に含まれていなければならず、構造体や共用体ならメンバが完全に定義されていなければならない。例えば次の2つのソースファイルがあったとする。

/* 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演算子をデータ型のメモリ上に占める大きさを結果とするように実装しなければならない。また(既述の例外を除いて)これはコンパイル時計算される演算子であり、アセンブリ言語上では単なる即値になる。

構造体のパディング[編集]

利用者定義型の大きさは境界調整のためにメンバの大きさの合計よりも大きくなることが規格では許されている。次のコードは多くの環境において8と出力される。

struct student {
    char grade; /* charは1バイト */
    int age /* intは4バイト */
};
 
printf("%zu", sizeof (struct student));

この理由は、多くのコンパイラでは通常ワード単位にデータを揃えるためであり、個々のメンバも境界を揃えられる。上の場合は、境界調整によってメンバ変数のageが次のワード単位に置かれるのである。このような構造体には境界調整のためにメンバ間やメンバの後ろに「パディング」と呼ばれる余分な隙間が置かれるのである。多くのCPUではデータがワード単位のメモリアドレスに置かれていた方が高速に読み書きでき、また中にはワード単位に揃えられていないと読み書きできないCPUもある[1]

D[編集]

D言語では全ての型が持っているプロパティとしてsizeofが用意されている。

void main()
{
    writefln(int.sizeof);
}

.NET[編集]

Marshal.SizeOfメソッド[2]を用いるとオブジェクトの大きさを取得可能である。そのほか、各言語に類似のものが用意されていることがある。

C++/CLI[編集]

C++/CLIのsizeof演算子はネイティブ型で用いる限り基本的にC++と同じである。しかし値クラスやジェネリック型引数に対して用いられたときにはコンパイル時定数でなくなる。また参照クラスやインタフェースに対して用いることはできない[3]

C#[編集]

C#のsizeof演算子は、組込の整数・文字型に対して用いた場合のみは値も決まっており定数となっているが、その他の型に使用した場合はコンパイル時定数とならない[4]

Visual Basic[編集]

Visual Basicでは、Len関数などが存在する。Lenは文字列を引数に与えると文字列の長さを返すが、その他の型の変数を与えると変数の大きさを返す。

ActiveBasic[編集]

ActiveBasicでは、Visual Basicと同様のLen組込関数を持っているほか、SizeOf組込関数を持っている。Lenは型名を指定することができないが、SizeOfは型名を指定できる。逆にSizeOfに変数や式を指定することはできない。

脚注[編集]

[ヘルプ]
  1. ^ Rentzsch, Jonathan. "Data alignment: Straighten up and fly right." www.ibm.com. 08 FEB 2005. Accessed 1 Oct 2006
  2. ^ Marshal.SizeOf メソッド”. MSDNライブラリ. マイクロソフト (2007年11月). 2010年3月7日閲覧。 “引用文”
  3. ^ うぇね. “Chapter 15 : 演算”. C++/CLI 言語仕様書 ECMA標準 372 うぇね翻訳. 2006年3月29日閲覧。
  4. ^ sizeof (C# リファレンス)”. MSDNライブラリ. 2009年8月29日閲覧。