Cのsize_tとは何ですか?


626

size_tCで混乱していsizeofます。オペレーターから返されることはわかっています。しかし、それは正確には何ですか?データ型ですか?

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

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

int i;または使用する必要がありますかsize_t i;


11
それらが唯一のオプションである場合intsome_size署名されている場合は署名されている場合に使用しますsize_t
2010年

8
@Nate不正解です。POSIXにはssize_tタイプがありますが、実際に使用する正しいタイプはptrdiff_tです。
Steven Stewart-Gallus

2
その答えは、低レベルプログラミングほどには明確ではありません。インテル®64でのC、アセンブリ、およびプログラムの実行です。本に記載されているように、インデックスを使用するint iだけでは、巨大な配列を扱うには不十分な場合があります。したがって、使用size_t iすることで、より多くのインデックスに対応できるため、問題にならないはずの巨大な配列がある場合でも使用できます。size_tデータ型です。通常はaですunsigned long intが、これはシステムによって異なります。
ブルーノ

回答:


461

ウィキペディアから

1999 ISO C標準(C99)によると、size_t少なくとも16ビットの符号なし整数型です(7.17および7.18.3を参照)。

size_tは、で定義されているC99 ISO / IEC 9899標準など、いくつかのC / C ++標準で定義されている符号なしデータ型stddef.hです。1stdlib.hこのファイルは内部的にサブインクルードされているため、インクルードすることでさらにインポートできます stddef.h

このタイプは、オブジェクトのサイズを表すために使用されます。サイズを取得または返すライブラリ関数は、それらが型であるか、戻り型がであると想定していますsize_t。さらに、最も頻繁に使用されるコンパイラベースの演算子sizeofは、と互換性のある定数値に評価する必要があり size_tます。

意味として、size_t配列のインデックスを保持することが保証されている型です。


4
「サイズを取得または返すライブラリ関数は、サイズが... size_tであると想定します」stat()がファイルのサイズにoff_tを使用することを除いて
Draemon

64
@Draemonそのコメントは根本的な混乱を反映しています。 size_tメモリ内のオブジェクト用です。C標準は、ディスクやファイルシステムとの関係stat()off_t(POSIXの定義です)何も定義していませんFILE。ストリームで停止します。仮想メモリ管理は、サイズ要件に関する限り、ファイルシステムやファイル管理とは完全に異なるため、off_tここでは言及しません。
jw013 2013年

3
@ jw013:基本的な混乱とは言えませんが、興味深い点を指摘します。それでも、引用されたテキストは「メモリ内オブジェクトのサイズ」を示しておらず、「オフセット」は、それが格納されている場所に関係なく、サイズタイプに適した名前ではありません。
ドラえもん2013年

30
@ドラえもん良い点。この回答は、私の意見ではウィキペディアを引用していますが、この場合は最も良い説明がありません。C標準自体はより明確です。それはsize_tsizeof演算子の結果の型として定義されています(7.17p2 about <stddef.h>)。セクション6.5は、C式がどのように機能するかを正確に説明しています(6.5.3.4 for sizeof)。sizeofディスクファイルには適用できないため(ほとんどの場合、Cはディスクとファイルの動作さえ定義していないため)、混乱する余地はありません。言い換えれば、ウィキペディアを非難することです(そしてウィキペディアを引用するためのこの回答であり、実際のC標準ではありません)。
jw013 2013年

2
@Draemon-「基本的な混乱」の評価にも同意します。C / C ++標準を読んでいない場合は、「オブジェクト」が「オブジェクト指向プログラミング」を指していると思われるかもしれませんが、そうではありません。これらのOOPオブジェクトはないが、オブジェクトはまだあるC標準を読んで、調べてください。答えはあなたを驚かせるかもしれません!
Heath Hunnicutt 2013年

220

size_t符号なしタイプです。したがって、負の値(<0)を表すことはできません。あなたは何かを数えるときにそれを使用し、それが負になることはあり得ないことを確信しています。たとえば、文字列の長さが少なくとも0でなければならないため、strlen()aを返します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ためです。この場合、ユースケースによっては、およびをint(またはlong long)使用した方がよい場合があります。s1s2

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


8
@Alok:2つの質問:1)のサイズはsize_t?2)なぜ私size_tは何かよりも好むべきunsigned intですか?
Lazer

2
@Lazer:のサイズはsize_tですsizeof(size_t)。C標準は、SIZE_MAX少なくとも65535 size_tであることを保証します。これ は、sizeof演算子によって返されるタイプであり、標準ライブラリで使用されます(たとえば、をstrlen返しますsize_t)。ブレンダンが言ったsize_tように、と同じである必要はありませんunsigned int
Alok Singhal

4
@Lazer-はい、size_t符号なしの型であることが保証されています。
Alok Singhal、2013年

2
@Celeritasいいえ、符号なしの型は負でない値しか表現できないことを意味します。「負の値を表すことはできない」と言ったほうがいいでしょう。
Alok Singhal 2013

4
@ JasonOster、2の補数はC標準の要件ではありません。の値がをs2 - s1オーバーフローしたint場合の動作は未定義です。
Alok Singhal 2015

73

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

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

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

これが私のマシンでsize_t定義されている方法ですstddef.h

typedef unsigned long size_t;

4
確かtypedef unsigned long size_tにコンパイラに依存しています。それとも常にそうであることを示唆していますか?
chux-モニカを2014年

4
@chux:実際、1つの実装で定義されているからといって、すべてがそうであるとは限りません。適例:64ビットWindows。unsigned long32ビット、size_t64ビットです。
TimČas2014

2
正確にsize_tの目的は何ですか?「int mysize_t;」のような自分用の変数を作成できる場合 または「long mysize_t」または「unsigned long mysize_t」。なぜ誰かがこの変数を作成してくれたのですか?
ミッドキン

1
@midkin size_tは変数ではありません。これは、メモリ内のオブジェクトのサイズを表現するときに使用できるタイプです。
Arjun Sreedharan

1
size_t32ビットマシンでは常に32ビットであり、64ビットも同様であるというのは本当ですか?
John Wu

70

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

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Ubuntu 14.04 64ビットGCC 4.8の出力:

typedef long unsigned int size_t;

GCC 4.2 stddef.hでは、glibcではなくGCCによって提供されることに注意してくださいsrc/gcc/ginclude/stddef.h

興味深いC99の外観

  • mallocsize_t引数として取るため、割り当て可能な最大サイズを決定します。

    そして、それもによって返されるためsizeof、配列の最大サイズを制限すると思います。

    参照:Cでの配列の最大サイズは?


1
私は同じ環境を持っていますが、GCCの「-m32」オプションを渡して32ビットでテストし、結果は「typedef unsigned int size_t」でした。この素晴らしいコマンド@Ciroを共有してくれてありがとう、とても助かりました!:-)
silvioprog 2017年

2
問題自体は混乱しない。多くの質問をし、多くの答えを出そうとするのは混乱する心です。この回答とArjun Sreedharanの回答が、人々の質問と回答を妨げるものではないことに驚いています。
バイオサイバーマン2017年

1
正解です。少なくとも人気のあるLinuxディストリビューションで、実際に何であるかsize_tわかります。
Andrey Portnoy、

25

types.hのマンページには次のように書かれています。

size_tは符号なし整数型です


19

まだ誰も言及していないため、の主な言語的重要性size_tは、sizeof演算子がその型の値を返すことです。同様に、の主な意味ptrdiff_tは、あるポインタを別のポインタから減算すると、その型の値が得られることです。それを受け入れるライブラリ関数は、そのような関数が、そのようなオブジェクトが存在する可能性のあるシステムでUINT_MAXを超えるサイズのオブジェクトを処理できるようにするためです。可能なすべてのオブジェクトに対して十分です。


私の質問は常にありました:sizeofが存在しなかった場合、size_tが必要になるでしょうか?
ディーンP

@DeanP:おそらくそうではありませんが、のようなものにどの引数型を使用するかという問題がありますmalloc()。個人的に、私は型の引数を取る見たバージョンを持つことが好きだろうintlongと、long long例えば実装短いタイプなどを推進し、いくつかの実装で、lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[一部のプラットフォームでは、呼び出しはするimalloc(123)呼び出しよりも安くなりlmalloc(123);、さらにはプラットフォーム上でsize_t16ありますビット、 `long`値で計算されたサイズを割り当てるコード...
スーパーキャット

...値がアロケータが処理できるよりも大きい場合、失敗した割り当てに依存できるはずです。
スーパーキャット

11

理由を説明します size_t存在する必要があったと、ここに到達する方法について説明します。

実用的な面では、size_tptrdiff_tは、64ビット実装では64ビット幅、32ビット実装では32ビット幅が保証されています。従来のコードを壊さずに、すべてのコンパイラで既存の型を強制することはできませんでした。

A size_t又はptrdiff_t必ずしも同じではないintptr_t、またはuintptr_t。彼らはまだ使用中だったときに、特定のアーキテクチャ上で異なっていたsize_tptrdiff_t(例えば16ビットWindowsなど)80年代後半には、標準に加え、C99は、多くの新しいタイプを追加したときに時代遅れになってきて、まだ行っていません。16ビットプロテクトモードのx86にはセグメント化されたメモリがあり、可能な最大の配列または構造は65,536バイトのサイズしかありませんでしたが、farポインターは32ビット幅で、レジスターよりも広い必要がありました。それらについてintptr_tは、32ビット幅でしたがsize_tptrdiff_t16ビット幅でレジスタに収まる可能性があります。そして、誰がどんな種類のオペレーティングシステムが将来書かれるかを知っていましたか?理論的には、i386アーキテクチャは、オペレーティングシステムが実際に使用したことがない48ビットポインターを備えた32ビットセグメンテーションモデルを提供します。

メモリオフセットの種類は、longレガシコードがlong32ビット幅であると想定しすぎているため、考えられません。この仮定は、UNIXおよびWindows APIにも組み込まれています。残念ながら、他の多くのレガシーコードもlong、ポインタ、ファイルオフセット、1970年から経過した秒数などを保持するのに十分な幅があると想定していました。POSIXは、前者の代わりに後者の仮定が真になるように強制する標準化された方法を提供しますが、どちらも移植可能な仮定ではありません。

int90年代のごく一部のコンパイラだけがint64ビット幅にしたからではありません。次に、long32ビット幅を維持することによって、彼らは本当に奇妙なことになりました。規格の次の改訂では、がintより広いことは違法であると宣言されましたが、ほとんどの64ビットシステムでは32ビット幅のままです。longint

それはあることができなかったlong long intことがあっても、32ビットシステムで広い少なくとも64ビットであるために作成されたので、とにかく後添加しました。

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


「64ビット実装では64ビット幅であることが保証されている」size_tなどに同意しptrdiff_tない。保証は誇張されている。の範囲size_tは、主に実装のメモリ容量によって決まります。「nビット実装」は、主に整数のネイティブプロセッサ幅です。確かに、多くの実装は同様のサイズのメモリとプロセッサバス幅を使用しますが、メモリが少ない広いネイティブ整数または大量のメモリを持つ狭いプロセッサが存在し、これら2つの実装プロパティを分離しています。
chux-モニカを

8

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

また注意してください size_t署名されていない。署名されたバージョンが必要な場合はssize_t、一部のプラットフォームにあり、例に関連します。

一般的なルールとして、私はintほとんどの一般的なケースに使用することをお勧めし、size_t/を使用するssize_t必要がある場合(mmap()たとえば)のみを使用します。


3

一般に、0から始めて上に行く場合は、常に符号なしの型を使用して、オーバーフローが負の値の状況に陥らないようにします。これは非常に重要です。配列の境界がたまたまループの最大値よりも小さいが、ループの最大値が型の最大値よりも偶然大きい場合、負の値にラップすると、セグメンテーション違反(SIGSEGV )。したがって、一般に、0から始まって上に行くループにintを使用しないでください。署名なしを使用します。


3
あなたの主張を受け入れることはできません。あなたは、オーバーフローバグが静かにあなたの配列内の有効なデータにアクセスすることにつながる方が良いと言いますか?
maf-soft 2016

1
@ maf-softは正しいです。エラーが検出されない場合は、プログラムのクラッシュよりも悪化します。なぜこの回答は賛成票を得たのですか?
yoyo_fun 2017年

配列内の有効なデータにアクセスする場合、符号なしの型は符号付きの型の制限でオーバーフローしないため、バグではありません。このロジックの人は何ですか?何らかの理由でcharを使用して256要素の配列を反復処理するとします。signedは127でオーバーフローし、128番目の要素はsigsegvになりますが、unsignedを使用すると、意図したとおりに配列全体が処理されます。繰り返しますが、intを使用している場合、配列は実際には20億要素を超えることはないため、どちらにしても問題ありません...
Purple Ice

1
整数オーバーフローが正か負かに関係なく、整数オーバーフローがバグではない状況は想像できません。segfaultを取得できないからといって、正しい動作が見られるとは限りません!そして、オフセットが正か負かに関係なく、セグメンテーション違反が発生する可能性があります。それはすべてあなたのメモリレイアウトに依存します。@PurpleIce、私はあなたがこの答えと同じことを言っているとは思わない。あなたの主張は、あなたが入れたい最大の値を保持するのに十分な大きさのデータ型を選択すべきだということであるように見えます。
Soren Bjornstad

とは言っても、意味的にはループインデックスに符号なしの型を使用することを好みます。変数が負にならない場合は、選択した型でそれを示すこともできます。また、GCCがこの特定の間違いを発見するのはかなりひどいものの、コンパイラーは値が負になってしまうバグを発見することもできます(ある場合には、unsignedを-1に初期化して警告を表示しませんでした)。同様に、size_tは、意味的に配列インデックスに適しています。
Soren Bjornstad

3

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


1

size_tループ変数は通常0以上であるため、または任意の符号なしの型がループ変数として使用される場合があります。

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

size_tは、0と0より大きい整数値のみを割り当てることができる符号なし整数データ型です。オブジェクトのサイズのバイトを測定し、sizeofオペレーターによって返されます。 constはの構文表現ですが、プログラムsize_tがなくconstても実行できます。

const size_t number;

size_t配列のインデックス付けとループカウントに定期的に使用されます。コンパイラが動作32-bitする場合unsigned int。コンパイラであれば、64-bitそれunsigned long long intも動作します。の最大サイズsize_tコンパイラのタイプ応じてます。

size_t既に上で定義し<stdio.h>たヘッダファイルが、それはまたによって定義することができ <stddef.h><stdlib.h><string.h><time.h><wchar.h>ヘッダー。

  • 例(const
#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

出力-: size = 800


  • 例(なしconst
#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

出力-: size = 800


-3

私の理解から、size_tありますunsignedそのビットサイズのネイティブなアーキテクチャのポインタを保持するのに十分な大きさである整数。

そう:

sizeof(size_t) >= sizeof(void*)

16
違います。ポインタのサイズはより大きい場合がありsize_tます。いくつかの例:x86リアルモードのCコンパイラは32ビットFARまたはHUGEポインタを持つことができますが、size_tは16ビットのままです。別の例:Watcom Cには、48ビット幅の拡張メモリ用の特別なファットポインターsize_tがありましたが、そうではありませんでした。ハーバードアーキテクチャのエンベデッドコントローラーでは、どちらも異なるアドレス空間に関係しているため、相関関係もありません。
PatrickSchlüter2013

1
そして、そのstackoverflow.com/questions/1572099/…に、128ビットポインタと32ビットを備えたAS / 400の例が他にもありますsize_t
PatrickSchlüter13年

これは露骨に誤りです。ただし、ここに保管しましょう
Antti Haapala
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.