2009-09-06 33 views
10

Nehmen Sie folgendes:C: Drucken große Zahlen

#include <stdio.h> 

main() { 
    unsigned long long verybig = 285212672; 

    printf("Without variable : %llu\n", 285212672); 
    printf("With variable : %llu", verybig); 
} 

Dies ist die Ausgabe des obigen Programms ist:

Without variable : 18035667472744448 
With variable : 285212672 

Wie Sie aus dem oben sehen können, wenn printf geben die Zahl als Konstante, druckt einige große falsche Zahl, aber wenn der Wert zuerst in einer Variablen gespeichert wird, druckt printf die richtige Zahl.

Was ist der Grund dafür?

Antwort

25

Versuchen Sie 285212672ULL; Wenn Sie es ohne Suffixe schreiben, werden Sie feststellen, dass der Compiler es als reguläre Ganzzahl behandelt. Der Grund dafür, dass es in einer Variablen funktioniert, ist, dass die ganze Zahl in der Zuweisung auf unsigned long long gesetzt wird, so dass der an printf() übergebene Wert der richtige Typ ist.

Und bevor Sie fragen, nein, der Compiler wahrscheinlich ist nicht intelligent genug, um es aus den "%llu ", um herauszufinden, in dem printf() Format-String. Das ist eine andere Ebene der Abstraktion. Der Compiler ist für die Sprachsyntax , printf() Semantik es ist nicht Teil der eine Laufzeitbibliothek Funktion, Syntax ist (nicht von Ihren eigenen Funktionen wirklich, außer dass es im Standard enthalten ist).

den folgenden Code für ein 32-Bit-int Betrachten und 64 -bit unsigned langes langes System:

die Ausgänge:

8589934593 
1 

Im ersten Fall sind die zwei 32-Bit-Ganzzahlen 1 und 2 sind auf den Stapel geschoben und printf() interpretiert, die als einzelnes 64-Bit-ULL Wert, 2 x 2 + 1. Das Argument 2 wird versehentlich in den ULL-Wert eingeschlossen.

In der zweiten drücken Sie tatsächlich die 64-Bit-1-Wert und eine überflüssige 32-Bit-Ganzzahl 2, die ignoriert wird.

Beachten Sie, dass dieses "Aussteigen" zwischen Ihrer Formatzeichenfolge und Ihren tatsächlichen Argumenten eine schlechte Idee ist. Etwas wie:

printf ("%llu %s %d\n", 0, "hello", 0); 

ist wahrscheinlich zum Absturz bringen, weil die 32-Bit-"hello" Zeiger werden von den %llu und %s verzehrt werden versuchen, das endgültigen 0 Argument de-Referenz. Das folgende "Bild" veranschaulicht dies (nehmen wir an, dass Zellen 32 Bits sind und dass die "Hallo" Zeichenfolge bei 0xbf000000 gespeichert wird.

What you pass  Stack frames  What printf() uses 
       +------------+ 
0    | 0   | \ 
       +------------+ > 64-bit value for %llu. 
"hello"   | 0xbf000000 |/
       +------------+ 
0    | 0   | value for %s (likely core dump here). 
       +------------+ 
       | ?   | value for %d (could be anything). 
       +------------+ 
+1

aber ich denke, Compiler ist intelligent genug,%, um herauszufinden, u in printf format spec, try printf ("% d% u", ~ 0, ~ 0) .. beide Werte werden wie erwartet ausgegeben. – sud03r

+0

Nein - diese Datentypen haben die gleiche Größe - es ist printf(), das herauszufinden - versuchen % d mit 'a'. – paxdiablo

+0

Pax: Das ist auch in Ordnung, Zeichen Literale sind Integer-Konstanten. – caf

3

285212672 ist ein int Wert. printf erwartet eine unsigned long long und Sie übergeben es eine int. Folglich werden mehr Bytes vom Stapel entfernt, als Sie einen echten Wert übergeben und Unsinn drucken. Wenn Sie es in eine unsigned long long Variable einfügen, bevor Sie es an die Funktion übergeben, wird es in der Zuweisungszeile auf unsigned long long hochgestuft, und Sie übergeben diesen Wert an printf, die ordnungsgemäß funktioniert.

0

Der Datentyp ist einfach eine Möglichkeit, den Inhalt eines Speicherplatzes zu interpretieren.
Im ersten Fall wird der konstante Wert im Nur-Lese-Speicher als int gespeichert, der printf versucht diese Adresse als 8-Byte-Stelle zu interpretieren, da der gespeicherte Wert lang ist und in diesem Fall einen ungültigen Wert ausgibt.
Im zweiten Fall versucht printf, einen langen langen Wert als 8 Bytes zu interpretieren und gibt an, was erwartet wird.

5

Es ist erwähnenswert, dass einige Compiler eine nützliche Warnung für diesen Fall geben - zum Beispiel, ist es das, was GCC über Ihren Code sagt:

x.c: In function ‘main’: 
x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’ 
+1

Ein weiterer Grund, kompilation * warnings * nicht zu ignorieren. – reuben