2009-09-06 7 views
10

Prendre les suivantes:C: Impression grand nombre

#include <stdio.h> 

main() { 
    unsigned long long verybig = 285212672; 

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

Ceci est la sortie du programme ci-dessus:

Without variable : 18035667472744448 
With variable : 285212672 

Comme vous pouvez le voir ci-dessus, lorsque printf est passé le nombre en tant que constante, il imprime un nombre incorrect énorme, mais lorsque la valeur est d'abord stockée dans une variable, printf imprime le nombre correct.

Quel est le raisonnement derrière cela?

Répondre

25

Essayez 285212672ULL; Si vous l'écrivez sans suffixe, vous trouverez que le compilateur le traite comme un entier normal. La raison pour laquelle cela fonctionne dans une variable est parce que l'entier est converti en unsigned long long dans l'affectation, de sorte que la valeur passée à printf() est le bon type.

Et avant de vous demander, non, le compilateur probablement n'est pas assez intelligent pour le comprendre de la "%llu "dans la chaîne de format printf(). Voilà un autre niveau d'abstraction. Le compilateur est responsable de la syntaxe du langage , printf() sémantique ne font pas partie de la syntaxe, il est une fonction de bibliothèque d'exécution (pas différent vraiment de vos propres fonctions, sauf qu'il est inclus dans la norme).

Considérez le code suivant pour un int 32 bits et 64 -bit système long non signé:

#include <stdio.h> 

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

qui fournit en sortie:

8589934593 
1 

Dans le premier cas, les deux entiers de 32 bits 1 et 2 sont poussés sur la pile et printf() interprète cela comme une seule valeur ULL 64 bits, 2 x 2 + 1. L'argument 2 est inclus par inadvertance dans la valeur ULL.

Dans la seconde, vous appuyez réellement sur la valeur 1 de 64 bits et un entier de 32 bits superflus 2, qui est ignoré. Notez que ce «désynchronisation» entre votre chaîne de format et vos arguments actuels est une mauvaise idée. Quelque chose comme:

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

est susceptible de tomber en panne parce que le pointeur de 32 bits "hello" sera consommé par le %llu et %s va essayer de de référence l'argument 0 final. L'image suivante illustre cela (supposons que les cellules sont de 32 bits et que la chaîne "hello" est stockée à 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

mais je pense que le compilateur est assez intelligent pour comprendre% u dans printf format spec, try printf ("% d% u", ~ 0, ~ 0) .. les deux vont imprimer les valeurs comme prévu .. – sud03r

+0

Non - ces types de données sont de la même taille - c'est printf() % d avec 'a'. – paxdiablo

+0

Pax: C'est bien aussi, les littéraux de caractère sont des constantes entières. – caf

3

285212672 est une valeur int. printf attend un unsigned long long et vous lui passez un int. Par conséquent, il faudra plus d'octets de la pile que vous avez passé une valeur réelle et imprime des déchets. Lorsque vous le placez dans une variable unsigned long long avant de la passer à la fonction, il sera promu à unsigned long long dans la ligne d'affectation et vous passez cette valeur à printf qui fonctionne correctement.

0

Le type de données est simplement une façon d'interpréter le contenu d'un emplacement de mémoire.
Dans le premier cas, la valeur constante est stockée dans un emplacement de mémoire morte en tant que int, printf essaye d'interpréter cette adresse comme 8 octets car il est indiqué que la valeur stockée est longue.
Dans le second cas, printf essaie d'interpréter une longue valeur longue de 8 octets et imprime ce qui est attendu.

5

Il est intéressant de souligner que certains compilateurs donnent un avertissement utile pour ce cas - par exemple, voici ce GCC dit à propos de votre code:

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

Encore une autre raison de ne pas ignorer les avertissements de compilation *. – reuben

Questions connexes