13

コンパイラ内でデータを失うことなく型キャストを行うにはどうすればよいですか?

例えば:

 int i = 10;
 UINT k = (UINT) k;

 float fl = 10.123;
 UINT  ufl = (UINT) fl; // data loss here?

 char *p = "Stackoverflow Rocks";
 unsigned char *up = (unsigned char *) p;

コンパイラはこのタイプの型キャストをどのように処理しますか? ビットを示す低レベルの例は高く評価されます。

4

4 に答える 4

20

まず、キャストとは、ある型の値を別の型の値に変換する明示的な要求であることに注意してください。また、キャストは常に新しいオブジェクトを生成します。これは、キャスト オペレーターによって一時的に返されるものです。ただし、参照型にキャストしても、新しいオブジェクトは作成されません。値によって参照されるオブジェクトは、別の型の参照として再解釈されます。

今あなたの質問に。変換には主に 2 つのタイプがあることに注意してください。

  • プロモーション: このタイプは、おそらくより狭いタイプからより広いタイプへのキャストと考えることができます。char から int へのキャスト、short から int へのキャスト、float から double へのキャストはすべてプロモーションです。
  • 変換: long から int、int から unsigned int などへのキャストが可能です。原則として、情報が失われる可能性があります。-1たとえば、署名されていない型付きオブジェクトに a を割り当てるとどうなるかについてのルールがあります。場合によっては、間違った変換により未定義の動作が発生する可能性があります。float が格納できるサイズよりも大きな double を float に割り当てた場合、動作は定義されません。

あなたのキャストを見てみましょう:

int i = 10; 
unsigned int k = (unsigned int) i; // :1

float fl = 10.123;
unsigned int  ufl = (unsigned int) fl; // :2

char *p = "Stackoverflow Rocks"; 
unsigned char *up = (unsigned char *) p; // :3
  1. このキャストにより、変換が発生します。によって 10 が格納されることが保証されているため、データの損失は発生しませんunsigned int。整数が負の場合、値は基本的に unsigned int の最大値をラップします ( 4.7/2を参照)。
  2. 10.123は 10 に切り捨てられます。ここで、明らかに情報が失われます。10 は unsigned int に収まるため、動作が定義されます。
  3. これは実際にはもっと注意が必要です。まず、文字列リテラルから への非推奨の変換がありますchar*。しかし、ここではそれを無視しましょう。(ここを参照)。さらに重要なことは、署名されていない型にキャストするとどうなるでしょうか? 実際には、その結果は5.2.10/7では規定されていません (そのキャストのセマンティクスは、この場合に reinterpret_cast を使用するのと同じであることに注意してください。それが可能な唯一の C++ キャストであるため):

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

したがって、char *再度キャストして戻した後にのみ、ポインターを安全に使用できます。

于 2008-12-04T17:01:20.770 に答える
8

この例の 2 つの C スタイルのキャストは、異なる種類のキャストです。C++ では、通常、それらを記述します。

unsigned int uf1 = static_cast<unsigned int>(fl);

unsigned char* up = reinterpret_cast<unsigned char*>(p);

1 つ目は、浮動小数点数を切り捨てる算術キャストを実行するため、データが失われます。

2 番目はデータを変更しません。ポインターを別の型として扱うようにコンパイラーに指示するだけです。この種のキャストには注意が必要です。非常に危険です。

于 2008-12-04T12:44:32.207 に答える
5

C および C++ の「型」は、変数がコンパイラで処理されるときに変数に割り当てられるプロパティです。このプロパティは、C++ の仮想関数/RTTI を除いて、実行時に存在しなくなりました。

コンパイラは、変数の型を使用して多くのことを決定します。たとえば、float を int に代入する場合、変換が必要であることがわかります。どちらのタイプもおそらく 32 ビットですが、意味が異なります。CPU に命令がある可能性がありますが、それ以外の場合、コンパイラは変換関数を呼び出すことを認識します。いえ & __stack[4] = float_to_int_bits(& __stack[0])

char* から unsigned char* への変換はさらに簡単です。それは単に別のラベルです。ビット レベルでは、p と up は同一です。コンパイラは、*p には符号拡張が必要ですが、*up には必要ないことを覚えておく必要があります。

于 2008-12-04T12:31:15.013 に答える
1

キャストは、その内容によって意味が異なります。それらは、表現されるビットを変更せずにデータ型の名前を変更することもできます (整数型とポインターの間のほとんどのキャストはこのようなものです)、または長ささえ保持しない変換 (ほとんどのコンパイラの double と int の間など) も可能です。 . 多くの場合、キャストの意味は単に指定されていません。つまり、コンパイラは合理的なことを行う必要がありますが、正確に何を文書化する必要はありません。

キャストの結果が使用可能な値になる必要さえありません。のようなもの char * cp; float * fp; cp = malloc(100); fp = (float *)(cp + 1); は、ほとんどの場合、float へのポインタのアライメントがずれてしまい、プログラムがそれを使用しようとすると、一部のシステムでプログラムがクラッシュします。

于 2008-12-04T17:45:17.140 に答える