24

そのオブジェクトを指すをにT変換することにより、タイプのオブジェクトの表現を見ることができます。少なくとも実際には:T*char*

int x = 511;
unsigned char* cp = (unsigned char*)&x;
std::cout << std::hex << std::setfill('0');
for (int i = 0; i < sizeof(int); i++) {
  std::cout << std::setw(2) << (int)cp[i] << ' ';
}

511これにより、私のシステムでの表現が出力されますff 01 00 00

ここでは(確かに)いくつかの実装定義の動作が発生しています。どのキャストでをに変換できint*ますunsigned char*か?また、そのキャストにはどの変換が必要ですか?キャストするとすぐに未定義動作を呼び出しますか?T*このようなタイプをキャストできますか?これを行うときに何を信頼できますか?

4

7 に答える 7

19

int*をに変換できるキャストはどれunsigned char*ですか?

この場合の C スタイルのキャストは と同じreinterpret_cast<unsigned char*>です。

このように T* 型をキャストできますか?

はいといいえ。yes の部分: (適切なand/or修飾子を使用して)任意のポインター型をchar*orに安全にキャストできます。結果は実装定義ですが、正当です。unsigned char*constvolatile

no の部分: 標準では、対象の型としてchar*andを明示的に許可しています。unsigned char*ただし、(たとえば) adouble*を anに安全にキャストすることはできませんint*。これを行うと、実装定義の動作から未定義の動作への境界を越えたことになります。厳密なエイリアシング規則に違反しています。

于 2012-12-21T19:26:00.673 に答える
6

キャストは次の場所にマップされます:

unsigned char* cp = reinterpret_cast<unsigned char*>(&x);

の基本的な表現intは実装で定義されており、それを文字として表示すると、それを調べることができます。あなたの場合、それは 32 ビットのリトルエンディアンです。

ここで特別なことは何もありません。内部表現を調べるこの方法は、どのデータ型にも有効です。

C++03 5.2.10.7: オブジェクトへのポインターは、異なる型のオブジェクトへのポインターに明示的に変換できます。ただし、「T1 へのポインター」型の右辺値を「T2 へのポインター」型 (ここで、T1 と T2 はオブジェクト型であり、T2 のアラインメント要件は T1 のアラインメント要件よりも厳密ではありません) に変換し、元の型に戻します。元のポインター値、そのようなポインター変換の結果は規定されていません。

これは、キャストによって不特定の動作が発生することを示唆しています。しかし、実用的に言えば、任意のポインター型から へのキャストchar*により、参照されるオブジェクトの内部表現を常に調べる (および変更する) ことができます。

于 2012-12-21T19:18:28.773 に答える
3

この例の実装動作は、システムのエンディアン属性です。この場合、CPU はリトル エンディアンです。
型キャストについては、あなたがしていることint*char*すべてにキャストするとき、何cpを指しているのかをcharとして解釈するようにコンパイラに指示しているので、最初のバイトのみを読み取り、それを文字として解釈します。

于 2012-12-21T19:18:07.327 に答える
3

この場合の C スタイルのキャストは、reinterpret_cast と同等です。標準では、5.2.10 のセマンティクスについて説明しています。具体的には、パラグラフ7で:

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

あなたの場合、アライメント要件が満たされ、結果が指定されていないことを意味します。

于 2012-12-21T19:26:53.710 に答える
1

すべてのポインタはメモリアドレスにすぎず、メモリ内のどのようなタイプも常にバイトのシーケンスと見なすことができるため、ポインタ間のキャストは常に可能です。

しかし、もちろん、シーケンスが形成される方法は、分解された型がメモリ内でどのように表現されるかに依存し、それは C++ 仕様の範囲外です。

とはいえ、非常に病理学的なケースでない限り、同じプラットフォーム (またはファミリ) のすべてのマシンに対して同じコンパイラによって生成されたすべてのコードでその表現が同じであると期待でき、異なるプラットフォームで同じ結果を期待するべきではありません。 .

一般に、避けるべきことの 1 つは、型サイズ間の関係を「事前定義」として表現することです。サンプルでは、sizeof(int) == 4*sizeof(char)​​それが常に正しいとは限りません。

ただし、sizeof(T) = N*sizeof(char) は常に真であるため、T は常に整数の char-s と見なすことができます。

于 2012-12-21T19:19:07.463 に答える
0

キャスト演算子がない限り、キャストは単にそのメモリ領域を別の方法で「見る」ように指示しているだけです。本当に空想的なものは何もありません。

次に、メモリ領域をバイト単位で読み取ります。変更しない限り、問題ありません。もちろん、表示される結果はプラットフォームによって大きく異なります。エンディアン、ワード サイズ、パディングなどについて考えてみてください。

于 2012-12-21T19:22:35.810 に答える
0

バイトオーダーを逆にするだけで、

00 00 01 ff

256 (01) + 255 (ff) = 511

これは、プラットフォームがリトル エンディアンであるためです。

于 2012-12-21T19:26:02.277 に答える