web-dev-qa-db-ja.com

Cのsize_tとは何ですか?

私はCのsize_tと混同しています。sizeof演算子によって返されることを私は知っています。しかし、それは正確には何ですか?それはデータ型ですか?

forループがあるとしましょう。

for(i = 0; i < some_size; i++)

int i;size_t i;のどちらを使うべきですか?

545
Vijay

Wikipediaより

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はあらゆる配列インデックスを保持することが保証されている型です。

414
sblom

size_tは符号なし型です。そのため、負の値(<0)を表すことはできません。あなたが何かを数えているとき、あなたはそれを使います、そしてそれが否定的になることはできないということを確信しています。例えば、 strlen() は、ストリングの長さを少なくとも0にする必要があるため、size_tを返します。

あなたの例では、あなたのループインデックスが常に0より大きくなることになっているなら、size_t、または他の任意の符号なしデータタイプを使用することは意味があるかもしれません。

size_tオブジェクトを使用するときは、算術演算を含むすべてのコンテキストでそれが使用されていることを確認する必要があります。負でない値が必要です。たとえば、あなたが持っているとしましょう:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

str2str1の長さの違いを見つけたいとします。できません。

int diff = s2 - s1; /* bad */

これは、diffに割り当てられた値は、s2 < s1の場合でも常に正の数になるためです。これは、計算が符号なし型で行われるためです。この場合、ユースケースが何であるかに応じて、long longおよびs1int(またはs2)を使用することをお勧めします。

C/POSIXにはsize_tを使用できる、または使用すべき関数がいくつかありますが、歴史的な理由のために使用できません。例えば、fgetsの2番目のパラメーターは理想的にはsize_tであるべきですが、intです。

200
Alok Singhal

size_tは任意の配列インデックスを保持できる型です。

実装に応じて、次のいずれかになります。

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

これが私のマシンのsize_tstddef.hがどのように定義されているかです:

typedef unsigned long size_t;
66

あなたが経験的なタイプなら

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は符号なし整数型になります

20
codaddict

まだ言及していないので、size_tの主な言語的意義は、sizeof演算子がその型の値を返すことです。同様に、ptrdiff_tの主な意味は、あるポインタから別のポインタを引くとその型の値が得られることです。大きい型のシステムでは "unsigned int"より大きな値を渡すコードを無駄に渡さずに、そのようなオブジェクトが存在する可能性があるシステムではサイズがUINT_MAXを超えるオブジェクトを処理できるため可能性のあるすべてのオブジェクトに十分です。

15
supercat

size_tintは互換性がありません。例えば64ビットのLinuxでは、size_tのサイズは64ビット(つまりsizeof(void*))ですが、intは32ビットです。

また、size_tは符号なしです。署名付きバージョンが必要な場合は、いくつかのプラットフォームでssize_tがあり、それはあなたの例により関連しているでしょう。

一般的な規則として、私はほとんどの一般的な場合にintを使用することをお勧めしますし、それが特に必要な場合にのみsize_t/ssize_tを使用します(例えばmmap()の場合)。

7
dtoux

なぜsize_tが存在する必要があるのか​​、そしてどのようにしてここに到達したのかを説明します。

実用的な用語では、size_tおよびptrdiff_tは、64ビット実装では64ビット幅、32ビット実装では32ビット幅などが保証されます。従来のコードを壊すことなく、すべてのコンパイラで既存の型を強制することはできませんでした。

size_tまたはptrdiff_tは、必ずしもintptr_tまたはuintptr_tと同じではありません。 80年代後半にsize_tptrdiff_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ビット幅を維持することで、本当に奇妙になりました。規格の次の改訂では、intlongより広いことは違法であると宣言されましたが、intはほとんどの64ビットシステムで32ビット幅のままです。

long long intにすることはできません。32ビットシステムでも少なくとも64ビット幅になるように作成されたため、とにかく後で追加されました。

そのため、新しいタイプが必要でした。そうでなかったとしても、他のすべてのタイプは、配列またはオブジェクト内のオフセット以外のものを意味していました。また、32ビットから64ビットへの移行という大失敗から1つの教訓が得られた場合、タイプに必要なプロパティを特定し、異なるプログラムで異なることを意味するプロパティを使用しないようにしました。

5
Davislor

一般的に、0から始めて上に向かっている場合は、オーバーフローを回避して負の値の状況に陥らないように、常に符号なしタイプを使用してください。配列の境界がループの最大値よりも小さい場合でも、ループの最大値が型の最大値よりも大きい場合は、負に折り返され、 セグメンテーションエラーが発生する可能性があるため、これは非常に重要です。 (SIGSEGV)そのため、一般的に、0から始まり上に向かうループにはintを使用しないでください。符号なしを使用してください。

3
Mark

size_tは符号なし整数データ型です。 GNU Cライブラリを使用しているシステムでは、これはunsigned intまたはunsigned long intになります。 size_tは、通常、配列のインデックス付けとループカウントに使用されます。

2
Prince

ループ変数は通常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
1