次の c プログラムの出力は次のとおりです。0.000000
出力の背後にロジックがありますか、または回答コンパイラに依存していますか、それともガベージ値を取得しているだけですか?
#include<stdio.h>
int main()
{
int x=10;
printf("%f", x);
return 0;
}
PS:- %f を使用して整数値を出力しようとするのはばかげていることはわかっています。私は理論的な観点からこれを尋ねているだけです。
次の c プログラムの出力は次のとおりです。0.000000
出力の背後にロジックがありますか、または回答コンパイラに依存していますか、それともガベージ値を取得しているだけですか?
#include<stdio.h>
int main()
{
int x=10;
printf("%f", x);
return 0;
}
PS:- %f を使用して整数値を出力しようとするのはばかげていることはわかっています。私は理論的な観点からこれを尋ねているだけです。
最新のC11 ドラフトから:
§7.16.1.1/2
...if type is not compatible with the type of the actual next argument
(as promoted according to the default argument promotions), the behavior
is undefined, except for the following cases:
— one type is a signed integer type, the other type is the corresponding
unsigned integer type, and the value is representable in both types;
— one type is pointer to void and the other is a pointer to a character type.
バイナリの整数 10 は次のようになるためです。
00000000 00000000 00000000 00001010
printf が行うことは、メモリ内表現を取得し、それを IEEE 754 浮動小数点数として提示しようとすることだけです。
浮動小数点数には 3 つの部分 (MSB から LSB まで) があります。
符号:1 ビット
指数:8 ビット
仮数:23 ビット
整数 10 は仮数ビットでちょうど 1010 であるため、printf の浮動小数点形式のデフォルトの精度よりもはるかに小さい非常に小さな数値です。
結果は定義されていません。
私は理論的な観点からこれを尋ねているだけです。
printf で何が起こるかは未定義ですが、以下のコードと非常によく似ている可能性があります(可変引数 IIRC の実際の実装によって異なります)。
免責事項: 以下は、すべてのプラットフォームで常に発生する真/有効な説明ではなく、1 つのプラットフォームで未定義の動作が発生した場合に何が発生する可能性があるかについて、より「そのように機能した場合」の説明です。
次のコードを想像してください。
int main()
{
int i = 10 ;
void * pi = &i ;
double * pf = (double *) pi ; /* oranges are apples ! */
double f = *pf ;
/* what is the value inside f ? */
return 0;
}
ここで、 double へのポインター (つまりpf
) が整数値 (つまりi
) をホストするアドレスを指しているため、得られるものは未定義であり、おそらくゴミです。
そのガベージの背後にある可能性があるものを本当に確認したい場合 (一部のプラットフォームでデバッグする場合)、次のコードを試してください。ここでは、共用体を使用して double または int データを書き込むメモリの一部をシミュレートします。
typedef union
{
char c[8] ; /* char is expected to be 1-byte wide */
double f ; /* double is expected to be 8-bytes wide */
int i ; /* int is expected to be 4-byte wide */
} MyUnion ;
f
andフィールドはi
値を設定するために使用され、c
フィールドはメモリをバイト単位で参照 (または変更) するために使用されます。
void printMyUnion(MyUnion * p)
{
printf("[%i %i %i %i %i %i %i %i]\n"
, p->c[0], p->c[1], p->c[2], p->c[3], p->c[4], p->c[5], p->c[6], p->c[7]) ;
}
上記の関数は、メモリ レイアウトをバイト単位で出力します。
以下の関数は、さまざまなタイプの値のメモリ レイアウトを表示します。
int main()
{
/* this will zero all the fields in the union */
memset(myUnion.c, 0, 8 * sizeof(char)) ;
printMyUnion(&myUnion) ; /* this should print only zeroes */
/* eg. [0 0 0 0 0 0 0 0] */
memset(myUnion.c, 0, 8 * sizeof(char)) ;
myUnion.i = 10 ;
printMyUnion(&myUnion) ; /* the representation of the int 10 in the union */
/* eg. [10 0 0 0 0 0 0 0] */
memset(myUnion.c, 0, 8 * sizeof(char)) ;
myUnion.f = 10 ;
printMyUnion(&myUnion) ; /* the representation of the double 10 in the union */
/* eg. [0 0 0 0 0 0 36 64] */
memset(myUnion.c, 0, 8 * sizeof(char)) ;
myUnion.f = 3.1415 ;
printMyUnion(&myUnion) ; /* the representation of the double 3.1415 in the union */
/* eg. [111 18 -125 -64 -54 33 9 64] */
return 0 ;
}
注: このコードは、Visual C++ 2010 でテストされました。
お使いのプラットフォームでそのように (またはまったく) 動作するという意味ではありませんが、通常は上記のような結果が得られるはずです。
最終的に、ガベージはメモリ内の 16 進数データ セットにすぎませんが、何らかのタイプとして認識されます。
ほとんどの型はデータのメモリ表現が異なるため、元の型以外の型のデータを見ると、ガベージ (またはそれほどガベージではない) の結果が得られます。
printf はそのように動作する可能性があるため、最初に int として設定されたときに生のメモリを double として解釈しようとします。
PS: int と double のバイト単位のサイズが異なるため、ガベージはさらに複雑になることに注意してください。
真剣に?
int main()
{
int x=10;
printf("%f",(double)(x));
return 0;
}
疑似コードを見て、printf に何が供給されているかを確認しましょう。
/* printf("...", [[10 0 0 0]]) ; */
printf("%i",x);
/* printf("...", [[10 0 0 0 ?? ?? ?? ??]]) ; */
printf("%f",x);
/* printf("...", [[0 0 0 0 0 0 36 64]]) ; */
printf("%f",(double)(x));
キャストは異なるメモリ レイアウトを提供し、整数の "10" データを double の "10.0" データに効果的に変更します。
したがって、"%i" を使用すると、[[?? ?? ?? ??]]、そして最初の printf では、[[10 0 0 0]] を受け取り、それを整数として正しく解釈します。
"%f" を使用すると、[[?? ?? ?? ?? ?? ?? ?? ??]]、2 番目の printf で [[10 0 0 0]] のようなものを受け取り、4 バイトが欠落しています。したがって、最後の 4 バイトはランダム データになります (おそらく [[10 0 0 0]] の「後」のバイト、つまり [[10 0 0 ?? ?? ?? ??]] のようなものです)
最後の printf では、キャストによって型が変更されたため、メモリ表現が [[0 0 0 0 0 0 36 64]] になり、printf はそれを double として正しく解釈します。
本質的にそれはゴミです。小さな整数は、存在してはならない正規化されていない浮動小数点数のように見えます。
次のように int 変数をキャストできます。
int i = 3;
printf("%f",(float)(i));