私はCのsize_t
と混同しています。sizeof
演算子によって返されることを私は知っています。しかし、それは正確には何ですか?それはデータ型ですか?
for
ループがあるとしましょう。
for(i = 0; i < some_size; i++)
int i;
とsize_t i;
のどちらを使うべきですか?
1999 ISO C規格(C99)によると、
size_t
は少なくとも16ビットの符号なし整数型です(7.17節と7.18.3節を参照)。
size_t
は、いくつかのC/C++標準で定義されている符号なしデータ型です。stddef.h
. 1 で定義されているC99 ISO/IEC 9899規格。このファイルの内部にはstdlib.h
が含まれているので、stddef.h
を含めることでさらにインポートできます。この型は、オブジェクトのサイズを表すために使用されます。サイズを取るか返すサイズのライブラリ関数は、それらが型であるか、戻り型が
size_t
であることを期待しています。さらに、最も頻繁に使用されるコンパイラベースの演算子sizeofは、size_t
と互換性のある定数値に評価されるべきです。
暗黙の内に、size_t
はあらゆる配列インデックスを保持することが保証されている型です。
size_t
は符号なし型です。そのため、負の値(<0)を表すことはできません。あなたが何かを数えているとき、あなたはそれを使います、そしてそれが否定的になることはできないということを確信しています。例えば、 strlen()
は、ストリングの長さを少なくとも0にする必要があるため、size_t
を返します。
あなたの例では、あなたのループインデックスが常に0より大きくなることになっているなら、size_t
、または他の任意の符号なしデータタイプを使用することは意味があるかもしれません。
size_t
オブジェクトを使用するときは、算術演算を含むすべてのコンテキストでそれが使用されていることを確認する必要があります。負でない値が必要です。たとえば、あなたが持っているとしましょう:
size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
str2
とstr1
の長さの違いを見つけたいとします。できません。
int diff = s2 - s1; /* bad */
これは、diff
に割り当てられた値は、s2 < s1
の場合でも常に正の数になるためです。これは、計算が符号なし型で行われるためです。この場合、ユースケースが何であるかに応じて、long long
およびs1
にint
(またはs2
)を使用することをお勧めします。
C/POSIXにはsize_t
を使用できる、または使用すべき関数がいくつかありますが、歴史的な理由のために使用できません。例えば、fgets
の2番目のパラメーターは理想的にはsize_t
であるべきですが、int
です。
size_t
は任意の配列インデックスを保持できる型です。
実装に応じて、次のいずれかになります。
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
これが私のマシンのsize_t
でstddef.h
がどのように定義されているかです:
typedef unsigned long size_t;
あなたが経験的なタイプなら 、
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Ubuntu 14.04 64ビットGCC 4.8の出力:
typedef long unsigned int size_t;
stddef.h
はGCCによって提供され、GCC 4.2のsrc/gcc/ginclude/stddef.h
の下のglibcではないことに注意してください。
興味深いC99の外観
malloc
は引数としてsize_t
を取るので、割り当て可能な最大サイズを決定します。
そしてそれはsizeof
によっても返されるので、私はそれがあらゆる配列の最大サイズを制限すると思います。
参照: Cの配列の最大サイズは?
types.h のマンページには、次のように記載されています。
size_tは符号なし整数型になります
まだ言及していないので、size_t
の主な言語的意義は、sizeof
演算子がその型の値を返すことです。同様に、ptrdiff_t
の主な意味は、あるポインタから別のポインタを引くとその型の値が得られることです。大きい型のシステムでは "unsigned int"より大きな値を渡すコードを無駄に渡さずに、そのようなオブジェクトが存在する可能性があるシステムではサイズがUINT_MAXを超えるオブジェクトを処理できるため可能性のあるすべてのオブジェクトに十分です。
size_t
とint
は互換性がありません。例えば64ビットのLinuxでは、size_t
のサイズは64ビット(つまりsizeof(void*)
)ですが、int
は32ビットです。
また、size_t
は符号なしです。署名付きバージョンが必要な場合は、いくつかのプラットフォームでssize_t
があり、それはあなたの例により関連しているでしょう。
一般的な規則として、私はほとんどの一般的な場合にint
を使用することをお勧めしますし、それが特に必要な場合にのみsize_t
/ssize_t
を使用します(例えばmmap()
の場合)。
なぜsize_t
が存在する必要があるのか、そしてどのようにしてここに到達したのかを説明します。
実用的な用語では、size_t
およびptrdiff_t
は、64ビット実装では64ビット幅、32ビット実装では32ビット幅などが保証されます。従来のコードを壊すことなく、すべてのコンパイラで既存の型を強制することはできませんでした。
size_t
またはptrdiff_t
は、必ずしもintptr_t
またはuintptr_t
と同じではありません。 80年代後半にsize_t
とptrdiff_t
がStandardに追加されたときにまだ使用されていた特定のアーキテクチャでは異なり、C99が多くの新しいタイプを追加したがまだ終了していないときに廃止されました16ビットWindows)。 16ビット保護モードのx86にはセグメントメモリがあり、可能な最大の配列または構造のサイズは65,536バイトのみでしたが、far
ポインターはレジスタよりも32ビット幅が必要でした。これらの場合、intptr_t
は32ビット幅でしたが、size_t
およびptrdiff_t
は16ビット幅でレジスタに収まる可能性があります。そして、将来どのようなオペレーティングシステムが作成される可能性があるのか、誰が知っていましたか?理論的には、i386アーキテクチャは、オペレーティングシステムが実際に使用したことのない48ビットポインターを持つ32ビットセグメンテーションモデルを提供します。
メモリオフセットのタイプをlong
にすることはできませんでした。これは、long
が正確に32ビット幅であると想定しているレガシーコードが多すぎるためです。この前提は、UNIXおよびWindows APIにも組み込まれていました。残念ながら、他の多くのレガシーコードも、long
がポインター、ファイルオフセット、1970年から経過した秒数などを保持するのに十分な幅であると想定していました。 POSIXは、前者ではなく後者の仮定を強制する標準化された方法を提供するようになりましたが、どちらも移植可能な仮定ではありません。
int
にすることはできませんでした。90年代のごく一部のコンパイラがint
64ビット幅にしたためです。その後、long
32ビット幅を維持することで、本当に奇妙になりました。規格の次の改訂では、int
がlong
より広いことは違法であると宣言されましたが、int
はほとんどの64ビットシステムで32ビット幅のままです。
long long int
にすることはできません。32ビットシステムでも少なくとも64ビット幅になるように作成されたため、とにかく後で追加されました。
そのため、新しいタイプが必要でした。そうでなかったとしても、他のすべてのタイプは、配列またはオブジェクト内のオフセット以外のものを意味していました。また、32ビットから64ビットへの移行という大失敗から1つの教訓が得られた場合、タイプに必要なプロパティを特定し、異なるプログラムで異なることを意味するプロパティを使用しないようにしました。
一般的に、0から始めて上に向かっている場合は、オーバーフローを回避して負の値の状況に陥らないように、常に符号なしタイプを使用してください。配列の境界がループの最大値よりも小さい場合でも、ループの最大値が型の最大値よりも大きい場合は、負に折り返され、 セグメンテーションエラーが発生する可能性があるため、これは非常に重要です。 (SIGSEGV)そのため、一般的に、0から始まり上に向かうループにはintを使用しないでください。符号なしを使用してください。
size_tは符号なし整数データ型です。 GNU Cライブラリを使用しているシステムでは、これはunsigned intまたはunsigned long intになります。 size_tは、通常、配列のインデックス付けとループカウントに使用されます。
ループ変数は通常0以上であるため、 size_t または任意の符号なし型がループ変数として使用されているように見える場合があります。
size_t オブジェクトを使用するときは、算術演算を含むすべての文脈で、負でない値だけが必要であることを確認する必要があります。たとえば、次のプログラムは間違いなく予想外の結果をもたらすでしょう:
// C program to demonstrate that size_t or
// any unsigned int type should be used
// carefully when used in a loop
#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];
// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;
// But reverse cycles are tricky for unsigned
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}
Output
Infinite loop and then segmentation fault