以下は明確に定義されていますか?
char* charPtr = new char[42];
int* intPtr = (int*)charPtr;
charPtr++;
intPtr = (int*) charPtr;
が適切に配置されていません(intPtr
少なくとも 2 つのケースのうちの 1 つで)。持っているだけで違法ですか?UBはどの段階で使用していますか?どのように使用でき、どのように使用できないのですか?
以下は明確に定義されていますか?
char* charPtr = new char[42];
int* intPtr = (int*)charPtr;
charPtr++;
intPtr = (int*) charPtr;
が適切に配置されていません(intPtr
少なくとも 2 つのケースのうちの 1 つで)。持っているだけで違法ですか?UBはどの段階で使用していますか?どのように使用でき、どのように使用できないのですか?
int
一般に、 のアラインメント要件が のアラインメント要件よりも大きい場合、結果は規定されません (5.2.10p7) char
。結果はその型の有効な値になるint *
ため、たとえば でポインタとして出力しoperator<<
たり、 に変換したりできますintptr_t
。
結果には未指定の値があるため、実装で指定されていない限り、それを間接的に指定し、結果のint
左辺値に対して左辺値から右辺値への変換を実行するのは未定義の動作です (未評価のコンテキストを除く)。への変換char *
は必ずしも往復ではありません。
ただし、オリジナルchar *
自体が from のキャストの結果である場合int *
、 to へのキャストはint *
往復の後半としてカウントされます。その場合、キャストが定義されます。
特に、 が式char *
の結果であった上記のケースでは、 である限り、ポインターが に対して適切にアラインされnew[]
ていることが保証されます (5.3.4p10) 。式は割り当て関数からストレージを取得するため、3.7.4.1p2 が適用されます。ポインターは、基本的なアラインメント要件を持つ任意の完全なオブジェクト型のポインターに変換でき、オブジェクトへのアクセスに使用できます [...]これは、5.3.4p10 への注記とともに、ポインターにも同じことが当てはまることを強く示唆しています。式によって返されます。この場合、は初期化されていないへのポインタですchar *
int
sizeof(int) <= 42
new[]
void *
char *
new[]
int *
int
オブジェクトであるため、そのインダイレクションで左辺値から右辺値への変換を実行することは未定義 (3.8p6) ですが、そのインダイレクションへの代入は完全に定義されています。int
オブジェクトは割り当てられたストレージ(3.7.4.1p2) にあるため、元にint *
戻すとchar *
1.8p6 あたりの元の値が得られます。これは、オブジェクトのアドレスでないchar *
限り、インクリメントされたポインターには当てはまりません。sizeof(int) == 1
int
最初に、もちろん: ポインタは最初のケースで (§5.3.4/10 と §3.7.4.1/2 によって) 整列されることが保証されており、両方のケースで正しく整列される可能性があります。(明らかに、 if ですsizeof(int) == 1
が、そうでない場合でも、実装には必ずしもアラインメント要件があるとは限りません。)
明確にするために、キャストはすべてreinterpret_cast
です。
それを超えて、これは興味深い質問です。私が知る限り、標準に関する限り、2 つのキャストに違いはないからです。変換の結果は規定されていません (§5.2.10/7 による)。に戻すchar*
と元の値になるという保証さえありません。(たとえば、int*
が a よりも小さいマシンでは明らかにそうではありませんchar*
。)
もちろん、実際には、標準では、戻り値がnew char[N]
それに適合する可能性のある値に対して十分に調整される必要があるため、次のことができることが保証されています。
intPtr = new (charPtr) int;
int
デフォルトのコンストラクターがノーオペレーションであることを考えると、これはキャストとまったく同じ効果があります。(そして、それを仮定し
sizeof(int) <= 42
ます。)したがって、最初の部分が失敗する実装を想像するのは難しいです。intPtr
合法的に入手した他の と同じように を使用できるはずです
intPtr
。そして、それを に戻すと、char*
どういうわけか元の値とは異なる値になるという考えchar*
はばかげているように思えます。
2 番目の部分では、すべての賭けが外れています。ポインターを逆参照することは絶対にできません (実装で別の方法が保証されている場合を除きます) char*
。(たとえば、achar*
を an
に変換するとint*
切り上げられる単語アドレスのマシンを想像してみてください。その後、逆に変換すると、元よりも大きい achar*
にsizeof(int)
なります。または、位置合わせされていないポインターを変換しようとすると、常に null ポインターが返されます。)