14

C ではint、 、float、およびこれらへのポインターのような単純なデータ型の両方をキャストできます。

ここで、ある型へのポインターから別の型の値に変換する場合 (たとえば*floatto からint)、キャストと逆参照の順序は問題ではないと想定していました。つまり、変数の場合float* pf、 があります(int) *pf == *((int*) pf)。数学の可換性のようなもの...

しかし、そうではないようです。テストプログラムを書きました:

#include <stdio.h>
int main(int argc, char *argv[]){
  float f = 3.3;
  float* pf = &f;
  int i1 = (int) (*pf);
  int i2 = *((int*) pf);
  printf("1: %d, 2: %d\n", i1, i2);
  return 0;
}

私のシステムでは、出力は

1: 3, 2: 1079194419

したがって、ポインターのキャストは、値のキャストとは異なる動作をするようです。

何故ですか?2 番目のバージョンが、私が思っていることをしないのはなぜですか?

そして、これはプラットフォームに依存していますか、それとも未定義の動作を何らかの形で呼び出していますか?

4

6 に答える 6

17

以下は、pf で float を取得し、それを整数に変換することを示しています。ここでのキャストは、float を整数に変換する要求です。コンパイラは、浮動小数点値を整数値に変換するコードを生成します。(float 値を整数に変換するのは「通常の」ことです。)

int i1 = (int) (*pf);

以下は、最初に pf が整数を指していると考えるようコンパイラーに強制し (pf が float へのポインターであるという事実を無視し)、次に整数値を取得します (ただし、整数値ではありません)。これは奇妙で危険なことです。この場合のキャストは、適切な変換を無効にします。コンパイラは、メモリでビットの単純なコピーを実行します (ゴミを生成します)。(そして、メモリの配置の問題もある可能性があります!)

int i2 = *((int*) pf);

2 番目のステートメントでは、ポインターを「変換」していません。メモリが何を指すかをコンパイラに伝えています(この例では、これは間違っています)。

これら 2 つのステートメントは、非常に異なることを行っています。

c は、異なる操作を記述するために同じ構文を使用する場合があることに注意してください。

=============

double は C のデフォルトの浮動小数点型であることに注意してください (数学ライブラリは通常 double 引数を使用します)。

于 2011-01-08T23:57:32.610 に答える
4

もちろんそうです!キャストは、メモリの一部のセクションを調べる方法をコンパイラに伝えます。次にメモリにアクセスすると、指定した方法に基づいてメモリ内のデータを解釈しようとします。次の例を使用すると、理解しやすくなります。

int main()
{
    char A[] = {0, 0, 0, 1 };
    int p = *((int*)A);
    int i = (int)*A;
    printf("%d %d\n", i, p);
    return 0; 
}

32 ビットのリトル エンディアン マシンでの出力は0 16777216. これは、(int*)AA を整数へのポインターとして扱うようにコンパイラーに指示するためです。したがって、 A を逆参照すると、 A から始まる 4 バイトが調べられます(as sizeof(int) == 4)。エンディアンを考慮した後、4 バイトの内容は 16777216 と評価されました。一方、*AA を逆参照して取得0し、それを(int)*Aキャストして 0 を取得します。

于 2011-01-08T14:31:58.283 に答える
4

最初に逆参照し、後で int にキャストすると、float から int へのキャストの通常の (切り捨て) 動作が得られます。最初に int へのポインターにキャストしてから逆参照すると、動作は標準で定義されません。通常、float を int として含むメモリを解釈する際に現れます。それがどのように機能するかについては、http://en.wikipedia.org/wiki/IEEE_754-2008を参照してください。

于 2011-01-08T14:17:39.427 に答える
2

キャスト、次に逆参照とは、「この別のものを指しているふりをして、指されていると思われる別のものを取得する」ことを意味します。

逆参照してからキャストするとは、「実際に指し示しているものを取得し、通常の規則に従って別のものに変換する」ことを意味します。

「通常のルール」では、同じ値を論理的に表す別の型の値を取得するためにビットを変更できます。実際に指していないものを指しているふりをすることはできません。

于 2011-01-08T14:57:25.090 に答える
1

標準の 6.5/7 によると、大まかに言えば、格納された値は有効な型または文字型と互換性がある必要があります。int i2 = *((int*) pf)したがって、このステートメントは明確に定義されていないと思います。

于 2011-01-08T19:32:27.453 に答える
1

int は、float と同じようにメモリ内で表現されません。あなたが見ているのは、ポインタがintへのものであると考えているコンパイラであり、実際にはfloatの未定義部分を見つけているときに、intを見つけようとしていると考えて32ビットのメモリを調べます。多数。

于 2011-01-08T14:17:18.333 に答える