web-dev-qa-db-ja.com

size_tとuintptr_t

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

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

そのため、C99では、規格はintptr_tおよびuintptr_t型を導入しました。これらは、ポインターを保持できることが保証されている署名付きおよび署名なしの型です。

uintptr_t p = (size_t) v;

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

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

これらのタイプはすべて、基本的に同じ機能の些細な異なるバージョンを提供していませんか?そうでない場合、なぜですか?他の人にはできないことの1つでできないことは何ですか?もしそうなら、なぜC99は2つの本質的に不要なタイプを言語に追加したのですか?

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

243
Chris Lutz

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

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

227
Alex Martelli

あなたの声明について:

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

これは実際には誤acyです(誤った推論に起因する誤解)(a)思考後者は前者の後に続きますが、実際にはそうではありません。

ポインターと配列インデックスはnotと同じです。配列を65536個の要素に制限するが、ポインターが任意の値を巨大な128ビットアドレス空間にアドレスできるようにする適合実装を想定することは非常に妥当です。

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

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


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

すべての子犬はかわいいです。このことはかわいいです。したがって、これは子犬でなければなりません。

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

これは、最初のステートメントと似ていますが、必ずしも2番目のステートメントを強制するわけではありません。

86
paxdiablo

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

単純な名前の違いが適切なものに適切な型を使用するのに十分な理由ではないでしょうか?

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

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

33
unwind

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

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

12
Michael Burr

私は想像します(そして、これはすべての型名に当てはまります)。

たとえば、Windowsではunsigned shortwchar_tのサイズは同じですが(と思う)、wchar_tの代わりにunsigned shortを使用すると、保存に使用する意図が示されます単なる任意の数字ではなく、ワイド文字。

5
dreamlax

後方と前方の両方を見て、さまざまな奇数ボールアーキテクチャがランドスケープに散在していることを思い出して、私は彼らがすべての既存のシステムをラップし、すべての可能な将来のシステムも提供しようとしていると確信しています。

確かに、物事の解決方法として、これまでそれほど多くのタイプは必要としませんでした。

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

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

また、その頃に存在していたものを見てください。無数の286リアルモードメモリモデルに加えて、CDC 60ビットWord/18ビットポインターメインフレームはどうですか? Crayシリーズはどうですか?通常のILP64、LP64、LLP64を気にしないでください。 (私は常にMicrosoftがLLP64を装っていると考えていました。それはP64だったはずです。)委員会がすべての拠点をカバーしようとしていることは確かに想像できます...

3
DigitalRoss