Cの「トラップ表現」とは何ですか(いくつかの例が役立つ場合があります)?これはC++に適用されますか?
このコードを考えると...
float f=3.5; int *pi = (int*)&f;
...そして、、同じバイナリ表現/パターンを持っていると仮定し
sizeof(int) == sizeof(float)
ますか?f
*pi
3 に答える
トラップ表現は、C99(C89ではなくIIRC)によって使用される包括的な用語であり、型が占めるスペースに収まるビットパターンを記述しますが、その型の値として使用されると未定義の動作をトリガーします。定義はセクション6.2.6.1p5(6.2.6のすべてに触手が含まれています)にあり、長くて混乱するため、ここでは引用しません。このようなビットパターンが存在するタイプは、トラップ表現を「持っている」と言われます。トラップ表現を持つためにタイプは必要ありませんが、標準でトラップ表現がない
unsigned char
ことが保証されているタイプは(6.2.6.1p5、6.2.6.2p1)のみです。この規格では、トラップ表現の2つの架空の例が示されていますが、どちらも実際のCPUが長年行ってきたことに対応していないため、混同することはありません。トラップ表現の良い例(遭遇する可能性のあるCPUでハードウェアレベルのトラップ表現として適格な唯一のもの)は、浮動小数点型のシグナリングNaNです。C99 Annex F(セクション2.1)は、IEC 60559がNaNの動作を詳細に指定している場合でも、シグナリングNaNの動作を明示的に未定義のままにします。
ポインタ型はトラップ表現を持つことができますが、nullポインタはトラップ表現ではないことに注意してください。ヌルポインタは、逆参照またはオフセットされている場合にのみ未定義の動作を引き起こします。それらに対する他の操作(最も重要なのは、比較とコピー)が明確に定義されていることです。トラップ表現を持つタイプを使用してトラップ表現を読み取るだけでは、トラップ表現は未定義の動作を引き起こします。(無効であるがnull以外のポインターがトラップ表現と見なされるか、または見なされる必要があるかどうかは、議論の対象です。CPUはそれらをそのように扱いませんが、コンパイラーはそうする可能性があります。)
表示するコードの動作は未定義ですが、これは、トラップ表現ではなく、ポインターエイリアシングルールによるものです。これは、を同じ表現でに変換する方法
float
ですint
(あなたが言うようにsizeof(float) == sizeof(int)
)int extract_int(float f) { union { int i; float f; } u; u.f = f; return u.i; }
このコードはC99で未指定(未定義ではない)の動作をします。これは基本的に、標準が生成される整数値を定義しないことを意味しますが、有効な整数値を取得します。これはトラップ表現ではなく、コンパイラーは最適化できません。あなたがこれをしていないという仮定の下で。(セクション6.2.6.1、パラ7。私のC99のコピーには、技術的なコリジェンダが含まれている可能性があります。これは元の出版物では定義されていませんでしたが、TCでは指定されていないものに変更されました。)
フロートをintへのポインタでエイリアスする未定義の動作。
一般に、トラップ以外のIEEE-754浮動小数点値は、一部のプラットフォームでは問題なく整数として表すことができます。ただし、すべての浮動小数点値が一意の整数表現を持っていると想定し、FPUにその値をロードさせると、予期しない動作が発生する可能性のある浮動小数点値があります。
(http://www.dmh2000.com/cpp/dswap.shtmlからの例)
たとえば、エンディアンの異なるCPU間でマーシャリングする必要があるFPデータを操作する場合、次のことを考えるかもしれません。
double swap(double)
残念ながら、コンパイラが入力をFPUレジスタにロードし、それがトラップ表現である場合、FPUは、たまたま異なるビット表現である同等のトラップ表現で入力を書き戻すことができます。
言い換えると、正しく変換しないと、対応するビット表現がないFP値がいくつかあります(正しいとは、、via、またはその他の標準メカニズムを意味しunion
ますmemcpy
)char *
。