2009-09-06 27 views
10

tome las siguientes:C: Impresión de grandes números de

#include <stdio.h> 

main() { 
    unsigned long long verybig = 285212672; 

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

Ésta es la salida del programa anterior:

Without variable : 18035667472744448 
With variable : 285212672 

Como se puede ver de lo anterior, cuando printf se pasa el número como constante, imprime un gran número incorrecto, pero cuando el valor se almacena primero en una variable, printf imprime el número correcto.

¿Cuál es el razonamiento detrás de esto?

Respuesta

25

Probar 285212672ULL; si lo escribe sin sufijos, encontrará que el compilador lo trata como un entero regular. La razón por la que está trabajando en una variable es porque el número entero se está transfiriendo a un unsigned long long en la asignación, de modo que el valor pasado a printf() es del tipo correcto.

Y antes de que preguntes, no, el compilador probablemente no es lo suficientemente inteligente como para comprenderlo a partir de la "%llu "en la cadena printf() formato. Eso es un nivel diferente de abstracción. El compilador es responsable de la sintaxis del lenguaje , printf() la semántica no son parte de la sintaxis, que es una función de la biblioteca de tiempo de ejecución (no se diferencia realmente de sus propias funciones excepto que está incluido en el estándar).

Considere el siguiente código para un int de 32 bits y 64 sistema de larga duración sin signo de bit:

#include <stdio.h> 

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

que da salida:

8589934593 
1 

En el primer caso, los dos enteros de 32 bits 1 y 2 son empujados en la pila y printf() interpreta que como un único valor ULL 64 bits, 2 x 2 + 1. El argumento 2 se incluye inadvertidamente en el valor ULL.

En el segundo, realmente empuja el valor 1 de 64 bits y un entero superfluo de 32 bits 2, que se ignora.

Tenga en cuenta que esta "salida de paso" entre su cadena de formato y sus argumentos reales es una mala idea. Algo así como:

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

es probable que fallen debido a que el puntero de 32 bits "hello" será consumido por el %llu y %s a tratar de retirar la información del argumento final 0. La siguiente "imagen" ilustra esto (supongamos que las celdas son de 32 bits y que la cadena "hello" se almacena 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

pero creo compilador es suficientemente inteligente como para averiguar% u en printf format spec, try printf ("% d% u", ~ 0, ~ 0) .. ambos imprimirán los valores como se espera. – sud03r

+0

No - esos tipos de datos son del mismo tamaño - es printf() descifrando eso - intente % d con 'a'. – paxdiablo

+0

Pax: Eso está bien también, los literales de caracteres son constantes enteras. – caf

3

285212672 es un valor de int. printf espera un unsigned long long y lo está pasando un int. En consecuencia, tomará más bytes de la pila de lo que pasó un valor real e imprime basura. Cuando lo coloca en una variable unsigned long long antes de pasarlo a la función, se promoverá a unsigned long long en la línea de asignación y usted pasará ese valor a printf que funciona correctamente.

0

Tipo de datos es simplemente una forma de interpretar los contenidos de una ubicación de memoria.
en el primer caso, el valor constante se almacena en la ubicación de memoria de solo lectura como int, printf intenta interpretar esta dirección como ubicación de 8 bytes ya que se indica que el valor almacenado es largo con el cual imprime el valor de basura.
En el segundo caso, printf intenta interpretar un valor largo como 8 bytes e imprime lo que se espera.

5

Vale la pena señalar que algunos compiladores dan una advertencia útil para este caso - por ejemplo, esto es lo que dice GCC acerca de su código:

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

Otra razón más para no ignorar las advertencias * de compilación. – reuben

Cuestiones relacionadas