厳密には、これは未定義の動作ではなく、実装によって定義されます。したがって、非主流のアーキテクチャーをサポートすることを計画している場合はお勧めできませんが、おそらくそうすることができます。
interjayによって与えられた標準的な引用はUBを示す良いものですが、それはポインター-ポインター算術を扱うので、私の意見では2番目に良いヒットにすぎません(面白いことに、一方は明示的にUBで、もう一方はそうではありません)。質問の操作を直接扱っている段落があります:
[expr.post.incr] / [expr.pre.incr]
オペランドは[...]または完全に定義されたオブジェクトタイプへのポインタです。
ああ、ちょっと待って、完全に定義されたオブジェクトタイプですか?それで全部です?つまり、本当にタイプしますか?それで、オブジェクトはまったく必要ないのですか?
そこに何かが明確に定義されていないかもしれないというヒントを実際に見つけるには、かなりの読書が必要です。これまでのところ、完全に許可されているかのように読み取られているため、制限はありません。
[basic.compound] 3
1つのポインタのタイプについて説明し、他の3つのいずれでもない場合、操作の結果は明らかに3.4:invalid pointerに分類されます。
ただし、無効なポインタを使用することは許可されていません。それどころか、ポインタが定期的に無効になる非常に一般的な通常の条件(たとえば、ストレージ期間の終了)がリストされています。つまり、それは明らかに許容できることです。本当に:
[basic.stc] 4
無効なポインター値を介した間接化および無効なポインター値を割り当て解除関数に渡すと、動作が未定義になります。無効なポインタ値を他の方法で使用すると、実装で定義された動作になります。
そこでは「その他」を実行しているため、未定義の動作ではなく、実装によって定義されるため、一般的には許可されます(実装が明示的に別のことを述べていない限り)。
残念ながら、これで話は終わりではありません。最終的な結果はこれ以降変更されませんが、「ポインタ」を検索する時間が長くなるほど、混乱が生じます。
[basic.compound]
オブジェクトポインタ型の有効な値は、メモリ内のバイトのアドレスまたはnullポインタを表します。タイプTのオブジェクトがアドレスAにある場合、[...]は、値の取得方法に関係なく、そのオブジェクトを指しているといいます。
[注:たとえば、配列の終わりを過ぎたアドレスは、そのアドレスにある可能性のある、配列の要素タイプの無関係なオブジェクトを指していると見なされます。[...]]。
次のように読みます。ポインターがメモリ内のどこかを指している限り、私は元気ですか?
[basic.stc.dynamic.safety]ポインター値は安全に派生したポインターです[何とか]
読み方:OK、安全に派生したものは何でも。これが何であるかを説明していませんし、私が実際にそれを必要としているとは言いません。安全に派生したもの。どうやら、私はまだ安全に派生していないポインタをうまく使うことができます。それらを逆参照することはおそらくそれほど良い考えではないと思いますが、それらを使用することは完全に許容されます。それはそうではありません。
実装では、ポインタの安全性が緩和されている場合があります。その場合、ポインタ値の有効性は、安全に派生したポインタ値であるかどうかには依存しません。
ああ、それで問題ないかもしれません。しかし、待って... "そうではない"?つまり、それもそうかもしれません。どうやって知るの?
または、実装は厳密なポインター安全性を備えている場合があります。その場合、参照される完全なオブジェクトが動的ストレージ期間であり、以前に到達可能と宣言されていない限り、安全に派生したポインター値ではないポインター値は無効なポインター値です。
待ってください、それで私declare_reachable()
はすべてのポインターを呼び出す必要がある可能性さえありますか?どうやって知るの?
これで、intptr_t
明確に定義されたに変換でき、安全に派生したポインタの整数表現を提供します。もちろん、これは整数なので、自由にインクリメントすることは完全に正当で明確です。
そして、はい、あなたはintptr_t
戻ってポインタも変換できます。これもまた明確に定義されています。元の値ではないだけで、安全な派生ポインタがあることは保証されません(明らかに)。それでも、全体として、標準の文字通り、実装で定義されていますが、これは100%正当なことです。
[expr.reinterpret.cast] 5
整数型または列挙型の値は、明示的にポインターに変換できます。十分なサイズの整数に変換されたポインター[...]と同じポインター型[...]の元の値に戻るポインター。ポインターと整数の間のマッピングは、それ以外の場合は実装定義です。
キャッチ
ポインターは単なる通常の整数であり、たまたまそれらをポインターとして使用します。ああ、それが本当なら!
残念ながら、それがまったく当てはまらないアーキテクチャが存在し、無効なポインタを生成するだけで(逆参照せず、ポインタレジスタに格納するだけで)トラップが発生します。
それが「実装定義」のベースです。それ、あなたはいつでもあなたがしてくださいと、ポインタをインクリメントしているという事実でし標準に対処する必要はありませんもちろん原因のオーバーフローの。アプリケーションのアドレス空間の終わりはオーバーフローの場所と一致しない可能性があり、特定のアーキテクチャー上のポインターのオーバーフローなどが存在するかどうかさえわかりません。全体としては、それは悪夢のような混乱であり、可能な利益とは関係ありません。
一方、過去のオブジェクトの状態を処理するのは簡単です。実装では、オブジェクトが割り当てられていないことを確認するだけで、アドレス空間の最後のバイトが占有されます。保証するのに便利で簡単なので、明確に定義されています。