9

優れたブログ投稿「未定義動作についてすべてのプログラマーが知っておくべきこと」の「タイプルール違反」のセクションには、次のように書かれています。

int*をfloat*にキャストし、それを逆参照する(「float」であるかのように「int」にアクセスする)ことは未定義の動作です。Cでは、これらの種類の型変換がmemcpyを介して行われる必要があります。ポインターキャストの使用は正しくなく、未定義の動作が発生します。このためのルールは非常に微妙であり、ここでは詳細に立ち入りたくありません(char *には例外があり、ベクトルには特別なプロパティがあり、共用体は物事を変更します)。

微妙なニュアンスでルールを理解したい。それらはC++11仕様のどこにありますか?またはそれが失敗した場合、C仕様(C90、C99、C11)?

このStackOverflowの質問N3485からリンクされているC++11仕様では、5.2.10「キャストの再解釈」を探していますが、char*またはunionsの例外の言語が表示されません。したがって、それはおそらく適切な場所ではありません。では、適切な場所はどこですか?

4

3 に答える 3

4

探しているルールは §3.10/10 (C++11) にあります。

プログラムが、次のいずれかの型以外の glvalue を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。 — オブジェクトの動的型。

— オブジェクトの動的型の cv 修飾バージョン

— オブジェクトの動的タイプに似たタイプ (4.4 で定義)

— オブジェクトの動的型に対応する符号付きまたは符号なしの型である型 — オブジェクトの動的型の cv 修飾バージョンに対応する符号付きまたは符号なしの型である型

— その要素または非静的データ メンバー (再帰的に、部分集合体または含まれるユニオンの要素または非静的データ メンバーを含む) に前述の型の 1 つを含む集約型または共用体型、

— オブジェクトの動的型の (おそらく cv 修飾された) 基本クラス型である型、

— char または unsignedchar 型。

未定義の動作にはさまざまな種類 (または動機) があります。

をキャストしint*float*逆参照する場合、何が起こるかはアーキテクチャと の値に依存するため、標準でそれを定義できないことは明らかですint。一方、引用された段落は完全に間違っています — を使用memcpyして変換を行うことも、ほとんど同じ理由で未定義の動作です。

未定義の動作の動機の 1 つは、実装がそれを定義できるようにすることです。ターゲット アーキテクチャが存在する場合は、そのアーキテクチャにとって意味のある方法で行います。これはそのような場合です。意図的に失敗させるコンパイラには欠陥があります。もちろん、32 ビットの 2 の補数 intと 32 ビットの IEEEfloatを想定すると、 の特定の値がintNaN のトラップに対応することが予想され、プログラムが失敗します。これは、動作が定義されていない理由の一部です。そのようなことが起こることを許可します。しかし、ハードウェアの低レベルの詳細に精通していれば、期待どおりに動作するはずですコンパイラはキャストを見ることができます。そうでない場合、これはコンパイラーの QoI 問題であり、そのようなタイプの作業ではそのようなコンパイラーを避ける必要があります。

上で示唆したように、この特定のケース、そして実際には、型パニング (例えば、共用体の 1 つのメンバーへの書き込みと別のメンバーからの読み取り) を含むすべてのケースで、標準がまだ解決していない問題を引き起こします。適切な言葉遣いを見つけます。この問題が発生するのは、通常、コンパイラは、異なる型 (文字型を除く) へのポインターがエイリアスされないと想定できるためです。がint*と同じオブジェクトを指すことは決してないfloat*. また、2 つのポインターがエイリアスできないことを証明することは、最適化にとって重要です。ポインター キャストまたは共用体が明確に表示されるコードを中断するコンパイラは、標準で未定義の動作であるとされていても、単に中断されているだけです。無関係な型への 2 つのポインターしか認識されないコードを中断するコンパイラーは、動作が明確に定義されていると標準で規定されている場合でも理解できます。

を使用memcpyすると、エイリアスのない 2 つの異なるオブジェクトを使用することで、この問題を回避できます。intan のビット パターンを afloatに配置してから float にアクセスすると、動作が定義されていないため、未定義の動作が発生します。float(またはその逆。a のビットをanにコピーintすると不正な値になる可能性があるマシンを少なくとも 1 台知っていintます。)

于 2013-02-06T15:27:55.487 に答える
1

C++ 標準では、特定の動作が定義されていると明示的に記述されていない場合、それは暗黙的に未定義であると規定されています。int*標準はを toにキャストする動作を定義していないため、float*暗黙的に未定義です。

于 2013-02-06T14:05:56.920 に答える
0

reinterpret_caststatic_caston ポインターは、 onvoidポインターの観点から定義されます。

5.2.10 キャストの再解釈 [expr.reinterpret.cast]>

7 オブジェクト ポインターは、異なる型のオブジェクト ポインターに明示的に変換できます。70 型「T1 へのポインター」の prvalue v が型「cv T2 へのポインター」に変換されるとき、結果はstatic_cast<cv T2*>(static_cast<cv void*>(v))、T1 と T2 の両方が標準レイアウト型 (3.9) であり、T2 のアライメント要件がより厳密でない場合です。 T1 のもの、またはいずれかのタイプが void の場合。「T1 へのポインター」型の prvalue を「T2 へのポインター」型 (T1 と T2 はオブジェクト型であり、T2 のアラインメント要件は T1 のアラインメント要件より厳密ではない) に変換し、元の型に戻すと、元の型が生成されます。ポインター値。他のそのようなポインタ変換の結果は規定されていません。

5.2.9 静的キャスト [expr.static.cast]

13 型「cv1 void へのポインター」の prvalue は、型「cv2 T へのポインター」の prvalue に変換できます。ここで、T はオブジェクト型であり、cv2 は cv1 と同じ cv 修飾、またはそれより大きい cv 修飾です。 . null ポインター値は、変換先の型の null ポインター値に変換されます。「cv void へのポインター」に変換されたオブジェクトへのポインター型の値は、場合によっては異なる cv 修飾を使用して、元の値を持つ必要があります。

于 2013-02-06T15:18:39.200 に答える