探しているルールは §3.10/10 (C++11) にあります。
プログラムが、次のいずれかの型以外の glvalue を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。 — オブジェクトの動的型。
— オブジェクトの動的型の cv 修飾バージョン
— オブジェクトの動的タイプに似たタイプ (4.4 で定義)
— オブジェクトの動的型に対応する符号付きまたは符号なしの型である型 — オブジェクトの動的型の cv 修飾バージョンに対応する符号付きまたは符号なしの型である型
— その要素または非静的データ メンバー (再帰的に、部分集合体または含まれるユニオンの要素または非静的データ メンバーを含む) に前述の型の 1 つを含む集約型または共用体型、
— オブジェクトの動的型の (おそらく cv 修飾された) 基本クラス型である型、
— char または unsignedchar 型。
未定義の動作にはさまざまな種類 (または動機) があります。
をキャストしint*
てfloat*
逆参照する場合、何が起こるかはアーキテクチャと の値に依存するため、標準でそれを定義できないことは明らかですint
。一方、引用された段落は完全に間違っています — を使用memcpy
して変換を行うことも、ほとんど同じ理由で未定義の動作です。
未定義の動作の動機の 1 つは、実装がそれを定義できるようにすることです。ターゲット アーキテクチャが存在する場合は、そのアーキテクチャにとって意味のある方法で行います。これはそのような場合です。意図的に失敗させるコンパイラには欠陥があります。もちろん、32 ビットの 2 の補数
int
と 32 ビットの IEEEfloat
を想定すると、 の特定の値がint
NaN のトラップに対応することが予想され、プログラムが失敗します。これは、動作が定義されていない理由の一部です。そのようなことが起こることを許可します。しかし、ハードウェアの低レベルの詳細に精通していれば、期待どおりに動作するはずです。コンパイラはキャストを見ることができます。そうでない場合、これはコンパイラーの QoI 問題であり、そのようなタイプの作業ではそのようなコンパイラーを避ける必要があります。
上で示唆したように、この特定のケース、そして実際には、型パニング (例えば、共用体の 1 つのメンバーへの書き込みと別のメンバーからの読み取り) を含むすべてのケースで、標準がまだ解決していない問題を引き起こします。適切な言葉遣いを見つけます。この問題が発生するのは、通常、コンパイラは、異なる型 (文字型を除く) へのポインターがエイリアスされないと想定できるためです。がint*
と同じオブジェクトを指すことは決してないfloat*
. また、2 つのポインターがエイリアスできないことを証明することは、最適化にとって重要です。ポインター キャストまたは共用体が明確に表示されるコードを中断するコンパイラは、標準で未定義の動作であるとされていても、単に中断されているだけです。無関係な型への 2 つのポインターしか認識されないコードを中断するコンパイラーは、動作が明確に定義されていると標準で規定されている場合でも理解できます。
を使用memcpy
すると、エイリアスのない 2 つの異なるオブジェクトを使用することで、この問題を回避できます。int
an のビット パターンを afloat
に配置してから float にアクセスすると、動作が定義されていないため、未定義の動作が発生します。float
(またはその逆。a のビットをanにコピーint
すると不正な値になる可能性があるマシンを少なくとも 1 台知っていint
ます。)