このプログラムはどのように機能しますか?


88
#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

表示します0!! そんなことがあるものか?理由は何ですか?


私は意図的に入れている%d中でprintfの挙動を研究するためのステートメントprintf

回答:


239

それ%dはを期待しているためですintが、フロートを提供しました。

%e/ %f/ %gを使用して、フロートを印刷します。


0が出力される理由:doubleに送信する前に、浮動小数点数がに変換されprintfます。リトルエンディアンの二重表現の数値1234.5は

00 00 00 00  00 4A 93 40

A %dは32ビット整数を消費するため、ゼロが出力されます。(テストとして、printf("%d, %d\n", 1234.5f);出力を得ることができます0, 1083394560。)


floatがに変換される理由についてdoubleint printf(const char*, ...)、printfのプロトタイプが6.5.2.2/7から、

関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数の昇格は、末尾の引数に対して実行されます。

そして6.5.2.2/6から、

呼び出された関数を表す式の型がプロトタイプを含まない場合、整数の昇格が各引数で実行され、型のfloatある引数がに昇格されdoubleます。これらは、デフォルトの引数プロモーションと呼ばれます

(これを見つけてくれてAlokに感謝します。)


4
+1ベストアンサー。「標準的に技術的に正しい」理由と「ありそうな実装」理由の両方に答えます。
Chris Lutz

12
彼が探していた答えを実際に提供してくれたのは、12人の中であなただけだと思います。
Gabe

11
のではprintf可変引数関数であり、標準は、可変長の機能のために、と言うfloatに変換されてdouble渡す前に。
Alok Singhal

8
C標準から:「関数プロトタイプ宣言子の省略表記により、最後に宣言されたパラメーターの後に引数の型変換が停止します。デフォルトの引数の昇格は、後続の引数に対して実行されます。」と「... float型の引数はdoubleに昇格されます。これらはデフォルトの引数昇格と呼ばれます。」
Alok Singhal

2
で不正なフォーマット指定子を使用すると、未定義の動作printf()呼び出されます。
Prasoon Saurav

45

技術的には何もありません、各ライブラリ独自の実装、およびので、勉強しようとするあなたの方法は、 printfprintfあなたが何をしているか実行しての行動は、多くの使用であることを行っていません。printfシステムでのの動作を調べようとしている可能性があります。その場合は、ドキュメントを読み、printfライブラリで使用できるかどうかのソースコードを確認する必要があります。

たとえば、私のMacbookでは、出力を取得します 1606416304、プログラムでています。

そうは言っても、float可変個の関数にa をfloat渡すと、はdouble。したがって、プログラムはaとして宣言したのと同じdoubleです。

のバイトを調べるには double SOの最近の質問に対するこの回答を見ることができます。

それをしましょう:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    unsigned char *p = (unsigned char *)&a;
    size_t i;

    printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
    for (i=0; i < sizeof a; ++i)
        printf("%02x ", p[i]);
    putchar('\n');
    return 0;
}

上記のプログラムを実行すると、次のようになります。

size of double: 8, int: 4
00 00 00 00 00 4a 93 40 

したがって、最初の4バイトはdouble0であることがわかりました。これが、0printf。これが、呼び出しの出力としてられたあります。

より興味深い結果を得るには、プログラムを少し変更します。

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}

Macbookで上記のプログラムを実行すると、次のようになります。

42 1606416384

Linuxマシンで同じプログラムを使用すると、次のようになります。

0 1083394560

なぜ印刷を逆方向にプログラムしたのですか?何か不足していますか?bがint = 42で、 '%d'が整数形式の場合、printf引数の2番目の変数であるため、2番目に出力される値ではないのですか?これはタイプミスですか?
Katastic Voyage 2017

おそらく、int引数が引数とは異なるレジスターで渡されるためdoubleです。 printfwith %dint42 の引数を取り、%d2番目のint引数がないため、2番目はおそらくジャンクを出力します。
Alok Singhal、2017年

20

%d指定子は伝えますprintf整数を期待します。したがって、floatの最初の4バイト(またはプラットフォームによっては2バイト)は整数として解釈されます。それらが偶然ゼロの場合、ゼロが出力されます

1234.5のバイナリ表現は次のようなものです

1.00110100101 * 2^10 (exponent is decimal ...)

float実際にIEEE754 double値として表現するCコンパイラーでは、バイトは(私がミスをしなかった場合)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

エンディアンがほとんどない(つまり、最下位バイトが最初に来る)Intel(x86)システムでは、このバイトシーケンスが逆になり、最初の4バイトがゼロになります。つまり、何がprintf印刷されるのか...

IEEE 754による浮動小数点表現については、このWikipediaの記事を参照してください。


7

これは、フロートがバイナリで表現されているためです。整数に変換すると、0になります。


1
彼が何を求めているのかを理解したのはあなただけです。もちろん私が自分を誤解しない限り。
Mizipzor

答えは不正確で不完全ですが、間違いではないことに同意します。
Shaihi

7

未定義の動作を呼び出したため、パラメータータイプについて嘘をつくことでprintf()メソッドの規約に違反したため、コンパイラーは自由に何でもできます。プログラムの出力を「dksjalk is a ninnyhead !!!」にすることができます。技術的にはまだ正しいでしょう。


5

その理由はprintf()、かなりおかしな関数だからです。タイプはまったくチェックしません。最初の引数がであるint(そしてこれがで言っていることである%d)と言った場合、それはあなたを信じており、に必要なバイトだけを取りますint。この場合、マシンが4バイトintと8 バイトを使用すると仮定するとdouble(これfloatはに変換されdoubleますprintf())、最初の4バイトはaちょうどゼロであり、これが出力されます。


3

floatを自動的に整数に変換しません。どちらも保存形式が異なるためです。したがって、変換したい場合は、(int)型キャストを使用します。

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}

2

C ++でもタグ付けしたので、このコードはおそらく予想どおりに変換を行います。

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

出力:

1234.5 1234


1

適切なフォーマット指定子(%d、%f、%sなど)と関連するデータ型(int、float、stringなど)を使用するだけです。


問題はそれをどのように修正するかではなく、なぜそれがどのように機能するかです。
ya23


0

0を印刷するために何かを印刷する必要がありました。Cでは0が他のすべてであることを忘れないでください!


1
どうですか?Cでは、他のすべてのものなどありません。
Vlad

xが何かの場合、!x == 0 :)
chunkyguy

if(iGot == "everything")print "everything"; それ以外の場合は「何も表示しない」。
nik

0

整数ではありません。を使用してみてください%f


問題は、フロートをintに変換して「1234」と表示しないのはなぜでしょうか。
ビョルンポレックス

cが論理的に意味のないことをさせないように期待しないでください。はい、多くの言語で1234が期待できます。cの実装によっては、この動作が定義されているとは思わないでしょう。Cを使うと、自分を親のようにぶら下げることができます。
2010年

Cは柔軟に設計されているためです。
tangrs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.