257

size_tC 標準では、 が任意の配列インデックスを保持できる型であることを保証しています。これは、論理的には、size_t任意のポインター型を保持できる必要があることを意味します。Google で見つけたいくつかのサイトで、これは合法であり、常に機能するはずであると読みました。

void *v = malloc(10);
size_t s = (size_t) v;

そのため、C99 では、標準でintptr_tanduintptr_t型が導入されました。これは、ポインターを保持できることが保証されている符号付きおよび符号なしの型です。

uintptr_t p = (size_t) v;

size_tでは、との使用の違いは何uintptr_tですか? どちらも符号なしで、どちらも任意のポインター型を保持できる必要があるため、機能的には同じように見えます。明快さ以外に、 a ではなくuintptr_t(またはさらに良いことに、 a )を使用する本当の説得力のある理由はありますか? フィールドが内部関数によってのみ処理される不透明な構造では、これを行わない理由はありますか?void *size_t

同様に、ptrdiff_tはポインターの違いを保持できる符号付きの型であり、したがってほとんどすべてのポインターを保持できるので、どのように と区別されintptr_tますか?

これらのタイプはすべて、基本的に同じ関数のわずかに異なるバージョンを提供しているのではないでしょうか? そうでない場合、なぜですか?そのうちの 1 つではできなくて、別の 1 つではできないことは何ですか? もしそうなら、なぜ C99 は 2 つの本質的に余分な型を言語に追加したのでしょうか?

関数ポインターは現在の問題には当てはまらないため、無視しても構いませんが、「正しい」答えの中心になるとこっそり疑っているので、気軽に言及してください。

4

7 に答える 7

249

size_t任意の配列インデックスを保持できる型です。これは、論理的に、 size_t が任意のポインター型を保持できる必要があることを意味します

必ずしも!たとえば、セグメント化された 16 ビット アーキテクチャの時代を思い出してください。配列は 1 つのセグメントに制限されている可能性があります (したがって、16 ビットでsize_tも可能です)。ただし、複数のセグメントを持つこともできます (したがって、intptr_t選択するには 32 ビット タイプが必要になります)。セグメントおよびその中のオフセット)。均一にアドレス可能なセグメント化されていないアーキテクチャの最近では、これらのことが奇妙に聞こえることはわかっていますが、標準は「2009年の標準」よりも幅広い多様性に対応する必要があります!-)

于 2009-09-23T06:05:46.460 に答える
91

あなたの声明について:

「C標準では、 が任意の配列インデックスを保持できる型であることを保証していsize_tます。これは、論理的に、size_t任意のポインター型を保持できる必要があることを意味します。」

これは実際には誤謬(誤った推論による誤解)です(a)後者は前者から派生していると思うかもしれませんが、実際にはそうではありません。

ポインターと配列インデックスは同じものではありません。配列を 65536 要素に制限するが、ポインターが任意の値を大規模な 128 ビット アドレス空間にアドレス指定できるようにする適合実装を想定するのは、非常にもっともらしいことです。

size_tC99 では、変数の上限はによって定義されSIZE_MAX、これは 65535 まで低くすることができると述べています (C11 で変更されていない C99 TR3、7.18.3 を参照)。最新のシステムでこの範囲に制限されている場合、ポインターはかなり制限されます。

実際には、おそらくあなたの仮定が成り立つことに気付くでしょうが、それは標準がそれを保証しているからではありません。実際にそれを保証するものではないからです。


(a)ところで、これは何らかの形の個人攻撃ではなく、批判的思考の文脈であなたの発言が間違っている理由を述べているだけです. たとえば、次の推論も無効です。

どの子犬もかわいいです。この事はかわいいです。したがって、これは子犬に違いありません。

子犬のかわいらしさなどはここでは関係ありません。私が述べているのは、最初の 2 つの文では子犬ではないかわいいものの存在が認められているため、2 つの事実が結論につながらないということだけです。

これは、必ずしも2番目のステートメントを義務付けるわけではない最初のステートメントに似ています。

于 2009-09-23T06:08:34.527 に答える
40

セグメントの制限、エキゾチックなアーキテクチャなどの理由については、他のすべての回答をそのままにしておきます。

名前の単純な違いは、適切なものに適切な型を使用するのに十分な理由ではありませんか?

サイズを保存する場合は、 を使用しますsize_t。ポインターを格納する場合は、 を使用しますintptr_t。あなたのコードを読んだ人は、「ああ、これはおそらくバイト単位の何かのサイズだ」、「ああ、これは何らかの理由で整数として格納されているポインタ値だ」とすぐにわかります。

それ以外の場合は、すべてunsigned longに (または、これらの現代ではunsigned long long) を使用できます。サイズがすべてではありません。型名には意味があり、プログラムを説明するのに役立ちます。

于 2009-09-23T07:55:34.717 に答える
12

最大配列のサイズがポインターより小さい可能性があります。セグメント化されたアーキテクチャについて考えてみてください。ポインタは 32 ビットかもしれませんが、1 つのセグメントは 64KB しかアドレス指定できない場合があります (たとえば、古いリアルモード 8086 アーキテクチャ)。

これらはもはやデスクトップ マシンでは一般的に使用されていませんが、C 標準は小規模で特殊なアーキテクチャをサポートすることを目的としています。たとえば、8 ビットまたは 16 ビットの CPU を使用して開発されている組み込みシステムがまだあります。

于 2009-09-23T06:05:05.877 に答える
5

コードで意図をよりよく伝えることができると思います (これはすべての型名に当てはまります)。

たとえば、Windows ではunsigned shortとは同じサイズですが (私が思うに)、代わりに使用すると、任意の数値ではなく、ワイド文字を格納するために使用するという意図が示されます。wchar_twchar_tunsigned short

于 2009-09-23T06:07:18.233 に答える
3

過去と未来の両方を見て、さまざまな奇妙なアーキテクチャが風景に散らばっていたことを思い出すと、それらがすべての既存のシステムをラップし、すべての可能な将来のシステムを提供しようとしていたことは間違いありません。

確かに、物事が落ち着いた方法では、これまでのところそれほど多くの型は必要ありませんでした。

しかし、かなり一般的なパラダイムである LP64 でも、システム コール インターフェイスに size_t と ssize_t が必要でした。完全な 64 ビット型を使用するとコストがかかり、4GB を超える I/O ops をパントしたいが、それでも 64 ビット ポインターを保持する、より制約のあるレガシーまたは将来のシステムを想像できます。

何が開発されたのか、将来何が起こるのか、疑問に思う必要があると思います。(おそらく 128 ビットの分散システムのインターネット全体のポインターですが、システム コールでは 64 ビット以下、あるいは「レガシー」の 32 ビット制限でさえあります。:-) レガシー システムが新しい C コンパイラを取得する可能性があるというイメージ.. .

また、当時の様子をご覧ください。無数の 286 個のリアルモード メモリ モデルの他に、CDC 60 ビット ワード / 18 ビット ポインタ メインフレームはどうですか? Crayシリーズはいかがですか?ノーマルのILP64、LP64、LLP64でも構いません。(Microsoft は LLP64 で大げさだといつも思っていましたが、P64 であるべきでした。) 委員会がすべての基盤をカバーしようとしているのは確かに想像できます...

于 2009-09-23T06:14:18.377 に答える
-11
int main(){
  int a[4]={0,1,5,3};
  int a0 = a[0];
  int a1 = *(a+1);
  int a2 = *(2+a);
  int a3 = 3[a];
  return a2;
}

intptr_t は常に size_t を代用する必要があり、逆もまた同様であることを意味します。

于 2011-02-04T11:45:04.840 に答える