最近、私は次のようなことに遭遇しました声明:
すべてのポインタが同じサイズになるのはよくあることですが、ポインタ型が異なるサイズを持つことは技術的には可能である。
しかし、私はこれそれは次のように述べています。
その間ポインタはすべて同じサイズですこれらはメモリ アドレスを格納するだけなので、何を指すのかを知っておく必要があります。
さて、上記の記述のうちどれが正しいのかはわかりません。2 番目に引用した記述は、フロリダ州立大学のコンピューター サイエンスの C++ ノートからの引用のようです。
私の意見では、すべてのポインターのサイズが同じであるべきである理由は次のとおりです。
1)次のようなものがあるとします。
int i = 0;
void* ptr = &i;
ここで、C++ 標準ではポインターが異なるサイズを持つことが許可されていると仮定します。さらに、任意のマシン/コンパイラー (標準で許可されているため) では、 a のvoid*
サイズは 2 バイトで、 a のint*
サイズは 4 バイトであると仮定します。
ここで問題があると思うのは、右側にはint*
4バイトのサイズの があり、左側にはvoid*
2バイトのサイズの があるということです。したがって、 から への暗黙的な変換が発生するとint*
、void*
情報の損失。
2)すべてのポインタはアドレスを保持します。特定のマシンではすべてのアドレスのサイズが同じなので、すべてのポインタのサイズも同じになるのは非常に自然 (論理的) です。
したがって、私は2番目の引用本当です。
私の最初の質問これについて C++ 標準では何と言っていますか?
2番目の質問問題は、C++ 標準がポインターが異なるサイズを持つことを許可しているのなら、それには理由があるということです。つまり、ポインターが異なるサイズを持つことを許可するのは、私には少し不自然に思えます (上で説明した 2 つの点を考慮すると)。したがって、標準委員会は、このこと (ポインターが異なるサイズを持つこと) をすでに考慮し、ポインターが異なるサイズを持つことを許可する理由をすでに持っていると確信しています。標準がポインターが異なるサイズを持つことを許可している場合にのみ、この質問 (2 番目の質問) をしていることに注意してください。
ベストアンサー1
「ポインタは単なるアドレスであり、アドレスは同じサイズの数値にすぎない」ため、すべてのポインタは同じサイズであると結論付けたくなるかもしれませんが、これは標準で保証されていないため、信頼することはできません。
C++ 標準では、次のことが明示的に保証されています。
void*
char*
(と同じサイズです)[基本.複合語]/5)T const*
、、はT volatile*
とT const volatile*
同じサイズですT*
。これは、同じ型のcv修飾バージョンがレイアウト互換、レイアウト互換型へのポインタは同じ値表現を持ちます([基本.複合]/3)。- 同様に、同じ基底型を持つ任意の2つの列挙型はレイアウト互換です([dcl.enum]/9) であるため、このような列挙型へのポインターのサイズは同じになります。
標準では保証されていませんが、実際には、すべてのクラス型へのポインターのサイズは同じであるということは基本的に常に当てはまります。その理由は次のとおりです。不完全なクラス型へのポインターは完全な型であるため、 が不完全なクラス型であるsizeof(T*)
場合でもコンパイラーに問い合わせることができ、 が定義された後の翻訳単位で再度T
コンパイラーに問い合わせた場合、結果は同じである必要があります。さらに、が宣言されている他のすべての翻訳単位でも、別の翻訳単位で完了しない場合でも、結果は同じである必要があります。したがって、sizeof(T*)
T
T
T*
コンパイラは、 の内部が何であるかを知らなくてものサイズを決定できなければなりませんT
。技術的には、コンパイラーは、クラス名が特定のプレフィックスで始まる場合、そのクラスのインスタンスをガベージ コレクションの対象にしたいと想定し、そのクラスへのポインターを他のポインターよりも長くするなど、いくつかのトリックを実行することがまだ許可されています。実際には、コンパイラーはこの自由を利用しないようで、異なるクラス タイプへのポインターは同じサイズであると想定できます。この想定に頼る場合は、static_assert
プログラムに を入れて、想定に違反する異常なプラットフォームをサポートしていないと言うことができます。
また、実際には、一般的には
- 任意の2つの関数ポインタ型は同じサイズを持ちます。
- データメンバー型への任意の2つのポインタは同じサイズを持ち、
- 関数メンバー型への任意の 2 つのポインターのサイズは同じになります。
その理由は、reinterpret_cast
ある関数ポインタ型から別の関数ポインタ型へ、そして元の型へ戻すことで、情報を失うことなく、いつでも可能であり、上記の他の2つのカテゴリについても同様だからです(式を再解釈するキャスト)。コンパイラは、異なる量のパディングを与えることで異なるサイズにすることができますが、これを行う実際的な理由はありません。
(しかし、MSVCにはモードがあるメンバーへのポインターは必ずしも同じサイズである必要はありません。これはパディングの量が異なるためではなく、単に標準に違反しているだけです。したがって、コードでこれに依存する場合は、おそらく を配置する必要がありますstatic_assert
。)
ニア ポインターとファー ポインターを持つセグメント化されたアーキテクチャを使用している場合、それらのサイズが同じであるとは考えないでください。これは、特定のポインター型のペアが一般に同じサイズであるという上記のルールの例外です。