Wie funktioniert dieses Programm?


88
#include <stdio.h>

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

Es zeigt ein 0!! Wie ist das möglich? Was ist die Begründung?


Ich habe absichtlich eine %din die printfErklärung aufgenommen, um das Verhalten von zu untersuchen printf.

Antworten:


239

Das liegt daran, %ddass erwartet wird, intaber Sie haben einen Float bereitgestellt.

Verwenden Sie %e/ %f/ %gden Schwimmer zu drucken.


Warum 0 gedruckt wird: Die Gleitkommazahl wird doublevor dem Senden an konvertiert printf. Die Zahl 1234.5 in doppelter Darstellung in Little Endian ist

00 00 00 00  00 4A 93 40

A %dverbraucht eine 32-Bit-Ganzzahl, sodass eine Null gedruckt wird. (Als Test könnten printf("%d, %d\n", 1234.5f);Sie Sie könnten auf Ausgabe bekommen 0, 1083394560.)


Warum wird der als Prototyp von printf von 6.5.2.2/7 floatkonvertiert ?doubleint printf(const char*, ...)

Die Auslassungsnotation in einem Funktionsprototyp-Deklarator bewirkt, dass die Konvertierung des Argumenttyps nach dem zuletzt deklarierten Parameter gestoppt wird. Die Standardargumentwerbung wird für nachfolgende Argumente durchgeführt.

und vom 6.5.2.2/6,

Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der keinen Prototyp enthält, werden die Ganzzahl-Heraufstufungen für jedes Argument ausgeführt und Argumente mit Typ floatwerden heraufgestuft double. Diese werden als Standardargumentwerbung bezeichnet .

(Danke Alok, dass du das herausgefunden hast.)


4
+1 Beste Antwort. Es beantwortet sowohl das "technisch korrekte Standard" Warum als auch das "Warum Ihrer wahrscheinlichen Implementierung".
Chris Lutz

12
Ich glaube, Sie sind der einzige von 12 Personen, der tatsächlich die Antwort gegeben hat, nach der er gesucht hat.
Gabe

11
Weil printfes sich um eine variadische Funktion handelt und der Standard besagt, dass für variadische Funktionen a vor dem Übergeben in a floatkonvertiert wird double.
Alok Singhal

8
Aus dem C-Standard: "Die Auslassungsnotation in einem Funktionsprototyp-Deklarator bewirkt, dass die Konvertierung des Argumenttyps nach dem zuletzt deklarierten Parameter gestoppt wird. Die Standardargument-Promotions werden für nachfolgende Argumente durchgeführt." und "... und Argumente mit dem Typ float werden auf double heraufgestuft. Diese werden als Standardargument-Heraufstufungen bezeichnet ."
Alok Singhal

2
Die Verwendung eines falschen Formatbezeichners in printf()ruft Undefiniertes Verhalten auf .
Prasoon Saurav

45

Technisch gibt sprechen , ist nicht die printf , die jeweils ihre Bibliothek implementiert eigene, und daher Ihre Methode der zu Studie versucht , printfindem Sie ‚Verhalten , was Sie tun , ist nicht von großem Nutzen sein wird. Möglicherweise versuchen Sie, das Verhalten printfIhres Systems zu untersuchen. In diesem Fall sollten Sie die Dokumentation lesen und im Quellcode nachsehen, printfob er für Ihre Bibliothek verfügbar ist.

Auf meinem Macbook erhalte ich beispielsweise die Ausgabe 1606416304mit Ihrem Programm.

Wenn Sie jedoch a floatan eine variable Funktion übergeben, floatwird das als übergeben double. Ihr Programm entspricht also der Deklaration aals double.

Um die Bytes von a zu untersuchen double, können Sie diese Antwort auf eine aktuelle Frage hier auf SO sehen.

Lass uns das tun:

#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;
}

Wenn ich das obige Programm ausführe, erhalte ich:

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

Die ersten vier Bytes von haben doublesich also als 0 herausgestellt, weshalb Sie möglicherweise 0die Ausgabe Ihres printfAnrufs erhalten haben.

Für interessantere Ergebnisse können wir das Programm ein wenig ändern:

#include <stdio.h>

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

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

Wenn ich das obige Programm auf meinem Macbook ausführe, erhalte ich:

42 1606416384

Mit dem gleichen Programm auf einem Linux-Computer bekomme ich:

0 1083394560

Warum haben Sie den Druck rückwärts programmiert? Vermisse ich etwas Wenn b ein int = 42 ist und '% d' das ganzzahlige Format ist, warum ist es nicht der zweite gedruckte Wert, da es die zweite Variable in den printf-Argumenten ist? Ist das ein Tippfehler?
Katastic Voyage

Wahrscheinlich, weil intArgumente in anderen Registern als doubleArgumente übergeben werden. printfmit %dnimmt das intArgument, das 42 ist, und das zweite %ddruckt wahrscheinlich Junk, da es kein zweites intArgument gab.
Alok Singhal

20

Der %dBezeichner gibt printfan, eine Ganzzahl zu erwarten. Die ersten vier (oder zwei, je nach Plattform) Bytes des Floats werden also als Ganzzahl interpretiert. Wenn sie zufällig Null sind, wird eine Null gedruckt

Die binäre Darstellung von 1234.5 ist so etwas wie

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

Bei einem C-Compiler, der floattatsächlich als IEEE754-Doppelwerte dargestellt wird, wären die Bytes (wenn ich keinen Fehler gemacht hätte)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

Auf einem Intel (x86) -System mit geringer Endianess (dh das niedrigstwertige Byte steht an erster Stelle) wird diese Bytesequenz umgekehrt, sodass die ersten vier Bytes Null sind. Das heißt, was wird printfgedruckt ...

In diesem Wikipedia-Artikel finden Sie eine Gleitkomma-Darstellung gemäß IEEE754.


7

Es liegt an der Darstellung eines Floats in Binärform. Bei der Konvertierung in eine Ganzzahl bleibt 0 übrig.


1
Sie scheinen der einzige zu sein, der verstanden hat, wonach er gefragt hat. Es sei denn, ich irre mich natürlich.
Mizipzor

Ich stimme zu, dass die Antwort ungenau und unvollständig ist, aber nicht falsch.
Shaihi

7

Weil Sie undefiniertes Verhalten aufgerufen haben: Sie haben den Vertrag der printf () -Methode verletzt, indem Sie sie über ihre Parametertypen belogen haben, sodass der Compiler frei tun kann, was er will. Es könnte dazu führen, dass das Programm "dksjalk is a ninnyhead !!!" und technisch wäre es immer noch richtig.


5

Der Grund ist, dass dies printf()eine ziemlich dumme Funktion ist. Typen werden überhaupt nicht überprüft. Wenn Sie sagen, dass das erste Argument ein ist int(und das ist es, womit Sie sagen %d), glaubt es Ihnen und es braucht nur die Bytes, die für ein benötigt werden int. In diesem Fall, wenn Ihr Computer vier Byte intund acht Byte verwendet double(das floatwird in ein doubleInneres konvertiert printf()), sind die ersten vier Bytes anur Nullen, und dies wird gedruckt.


3

Float wird nicht automatisch in eine Ganzzahl konvertiert. Weil beide unterschiedliche Speicherformate haben. Wenn Sie also konvertieren möchten, verwenden Sie (int) typecasting.

#include <stdio.h>

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

2

Da Sie es auch mit C ++ markiert haben, führt dieser Code die Konvertierung wie erwartet durch:

#include <iostream.h>

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

Ausgabe:

1234.5 1234

1

%d ist dezimal

%f ist float

Weitere davon finden Sie hier .

Sie erhalten 0, weil Gleitkommazahlen und Ganzzahlen unterschiedlich dargestellt werden.


1

Sie müssen nur den entsprechenden Formatbezeichner (% d,% f,% s usw.) Mit dem entsprechenden Datentyp (int, float, string usw.) verwenden.


Die Frage ist nicht, wie man es behebt, sondern warum es so funktioniert, wie es funktioniert.
ya23


0

Hey, es musste etwas drucken, damit es eine 0 druckte. Denken Sie daran, in C 0 ist alles andere!


1
Wie ist es so In C gibt es nicht alles andere.
Vlad

wenn x etwas ist, dann! x == 0 :)
chunkyguy

if (iGot == "alles") druckt "alles"; sonst "nichts" drucken;
Nik

0

Es ist keine ganze Zahl. Versuchen Sie es mit %f.


Ich denke die Frage ist, warum konvertiert es nicht einfach den Float in int und zeigt "1234" an?
Björn Pollex

Erwarten Sie nicht, dass c Sie nicht etwas tun lässt, das keinen logischen Sinn ergibt. Ja, viele Sprachen würden den 1234 geben, den Sie vielleicht erwarten, und vielleicht sogar einige Implementierungen von c, glaube ich nicht, dass dieses Verhalten definiert ist. Mit C kannst du dich aufhängen wie mit dem Elternteil, mit dem du versuchen kannst, verdammt noch mal zu knacken.
Wiederholung

Weil C flexibel ausgelegt ist.
Tangrs
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.