2009-09-06 15 views
10

adottare le seguenti:C: Stampa di grandi numeri

#include <stdio.h> 

main() { 
    unsigned long long verybig = 285212672; 

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

Questa è l'uscita del programma di cui sopra:

Without variable : 18035667472744448 
With variable : 285212672 

Come si può vedere da quanto sopra, quando printf viene passato il numero come costante, stampa un numero enorme errato, ma quando il valore viene prima memorizzato in una variabile, printf stampa il numero corretto.

Qual è il ragionamento alla base di questo?

risposta

25

Prova 285212672ULL; se lo scrivi senza suffissi, troverai che il compilatore lo considera come un normale intero. La ragione per cui sta funzionando in una variabile è perché il numero intero viene assegnato a un unsigned long long nel compito, in modo che il valore passato a printf() sia il tipo giusto.

E prima di chiedere, no, il compilatore probabilmente non è abbastanza intelligente per capirlo dal "%llu "nella stringa di formato printf(). Questo è un diverso livello di astrazione. Il compilatore è responsabile per la sintassi del linguaggio , printf() la semantica non fanno parte del sintassi, si tratta di una funzione di libreria di runtime (non è diverso in realtà dalle proprie funzioni, tranne che è incluso nella serie).

si consideri il seguente codice per un int a 32 bit e 64 a lungo non firmato sistema lungo:

#include <stdio.h> 

int main (void) { 
    printf ("%llu\n",1,2); 
    printf ("%llu\n",1ULL,2); 
    return 0; 
} 

che emette:

8589934593 
1 

Nel primo caso, i due interi a 32-bit 1 e 2 sono inseriti nello stack e printf() interpreta che come un singolo valore ULL 64 bit, 2 x 2 + 1. L'argomento 2 viene incluso inavvertitamente nel valore ULL.

Nel secondo, si spinge effettivamente il valore 1 a 64 bit e un numero intero superfluo a 32 bit 2, che viene ignorato.

Si noti che questa "uscita di passaggio" tra la stringa del formato e gli argomenti effettivi è una cattiva idea. Qualcosa di simile:

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

rischia di schiantarsi perché il puntatore a 32 bit "hello" sarà consumato dal %llu e %s cercherà di de-riferimento l'argomento finale 0. La seguente "immagine" illustra questo (supponiamo che le celle siano a 32 bit e che la stringa "ciao" sia archiviata a 0xbf000000.

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

ma penso compilatore è abbastanza intelligente per capire% u in formato printf, prova printf ("% d% u", ~ 0, ~ 0) .. entrambi stamperanno i valori come previsto. – sud03r

+0

No - questi tipi di dati hanno le stesse dimensioni - è printf() capito che - prova % d con 'a'. – paxdiablo

+0

Pax: Va bene, i caratteri letterali sono costanti intere. – caf

3

285212672 è un valore int. printf si aspetta un unsigned long long e lo stai passando a int. Di conseguenza, ci vorranno più byte fuori dallo stack rispetto a quando hai passato un valore reale e stampato spazzatura. Quando lo inserisci in una variabile unsigned long long prima di passarlo alla funzione, verrà promosso a unsigned long long nella riga di assegnazione e verrà passato a printf che funziona correttamente.

0

Datatype è semplicemente un modo di interpretare il contenuto di una locazione di memoria.
nel primo caso il valore costante viene archiviato in posizione di memoria di sola lettura come int, il printf tenta di interpretare questo indirizzo come ubicazione a 8 byte quando viene indicato che il valore memorizzato è lungo molto tempo durante il quale viene stampato il valore immondizia.
Nel secondo caso printf tenta di interpretare un valore lungo lungo come 8 byte e stampa ciò che è previsto.

5

Vale la pena sottolineare che alcuni compilatori dare un avvertimento utile per questo caso - per esempio, questo è quello che dice GCC sul codice:

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

Un altro motivo per non ignorare gli avvisi * di compilation *. – reuben