2010-03-26 37 views
258

Tengo una variable del tipo size_t, y quiero imprimir usando printf(). ¿Qué especificador de formato utilizo para imprimirlo de forma portátil?¿Cómo se puede imprimir una variable size_t de forma portátil utilizando la familia printf?

En la máquina de 32 bits, %u parece correcto. Recopilé con g ++ -g -W -Wall -Werror -ansi -pedantic, y no hubo ninguna advertencia. Pero cuando compilo ese código en una máquina de 64 bits, produce una advertencia.

size_t x = <something>; 
printf("size = %u\n", x); 

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int' 

La advertencia se va, como era de esperar, si cambio de que a %lu.

La pregunta es, ¿cómo puedo escribir el código, para que compile la advertencia gratuita en ambas máquinas de 32 y 64 bits?

Editar: Supongo que una respuesta podría ser "lanzar" la variable en un unsigned long, e imprimir usando %lu. Eso funcionaría en ambos casos. Estoy buscando si hay alguna otra idea.

+2

fundición a 'long' sin signo es la mejor opción si su aplicación libc no admite el modificador' z'; el estándar C99 recomienda 'size_t' no tener un rango de conversión entero mayor que' long', por lo que es razonablemente seguro – Christoph

+0

posible duplicado de [Platform independent size_t Especificadores de formato en c?] (http://stackoverflow.com/questions/2125845/platform-independent-size-t-format-specifiers-in-c/22114959#22114959) – maxschlepzig

+0

[¿Cuál es la forma correcta de usar printf para imprimir un size_t?] (http://stackoverflow.com/q/940087/995714) –

Respuesta

305

Usa el z modificador:

size_t x = ...; 
ssize_t y = ...; 
printf("%zu\n", x); // prints as unsigned decimal 
printf("%zx\n", x); // prints as hex 
printf("%zd\n", y); // prints as signed decimal 
+3

+1. ¿Es esta una adición de C99 o esto se aplica también a C++ (no tengo C90 a mano)? – avakar

+0

Creo que esta es una adición C99, ver §7.19.6.1.7. También hay 'j' para' intmax_t'/'uintmax_t' y' t' para 'ptrdiff_t'. –

+3

es una adición C99 y no aparece en la lista de 'printf()' modificadores de longitud del borrador de C++ 0x desde 2009-11-09 (tabla 84 en la página 672) – Christoph

5
printf("size = %zu\n", sizeof(thing)); 
0

¿Le avisará si pasa un entero sin signo de 32 bits a un formato% lu? Debería estar bien ya que la conversión está bien definida y no pierde ninguna información.

He oído que algunas plataformas definen macros en <inttypes.h> que puede insertar en el literal de cadena de formato, pero no veo ese encabezado en mi compilador de Windows C++, lo que implica que puede no ser multiplataforma.

+1

La mayoría de los compiladores no le avisarán si pasa algo del tamaño incorrecto en printf. GCC es una excepción. inttypes.h se definió en C99 por lo que cualquier compilador de C que sea compatible con C99 lo tendrá, que deberían ser todos ellos ahora. Aún así, es posible que deba activar C99 con un indicador de compilación. En cualquier caso, intttypes.h no define un formato específico para size_t o ptrdiff_t, ya que se decidió que eran lo suficientemente importantes como para obtener sus propios especificadores de tamaño de 'z' y 't', respectivamente. – swestrup

+0

Si usa '% lu', debe convertir el valor' size_t' en 'unsigned long'. No hay conversión implícita (aparte de promociones) para argumentos a 'printf'. –

5
std::size_t s = 1024; 
std::cout << s; // or any other kind of stream like stringstream! 
+0

Hay una etiqueta "C++". Creo que esta es la mejor manera en C++ :) – AraK

+7

Sí, pero el interlocutor pregunta específicamente por un especificador 'printf'. Supongo que tienen otras limitaciones no declaradas que hacen que el uso de 'std :: cout' sea un problema. –

+1

@Donal Me pregunto qué tipo de problema podrían crear secuencias de C++ en un proyecto de C++. – AraK

63

parece que varía dependiendo de qué compilador que está utilizando (Blech):

  • gnu says %zu (o %zx, o %zd sino que lo muestra como si estuviera firmado, etc.)
  • Microsoft says %Iu (o %Ix o %Id pero una vez más que está firmado, etc.)   — pero a partir de V19 cl (en Visual Studio 2015), Microsoft apoya %zu(ver this reply a this comment)

... y por supuesto, si usted está usando C++, puede utilizar cout en cambio, como suggested by AraK.

+2

'z' también es compatible con newlib (es decir, cygwin) – Christoph

+4

'% zd' es incorrecto para 'size_t'; es correcto para el tipo firmado que corresponde a 'size_t', pero' size_t' en sí mismo es un tipo sin firmar. –

+0

@KeithThompson: mencioné '% zu' también (y'% zx' en caso de que quieran hexadecimal). Es muy cierto que '% zu' probablemente debería haber sido el primero en la lista. Fijo. –

-1

C99 define "% zd" etc. para eso. (gracias a los comentaristas) No existe un especificador de formato portátil para eso en C++: podría usar %p, lo que significaría una palabra en estos dos escenarios, pero tampoco es una opción portátil, y da el valor en hexadecimal.

Como alternativa, utilice algunas transmisiones (por ejemplo, secuencia de cadenas) o una sustitución de impresión segura como Boost Format. Entiendo que este consejo solo tiene un uso limitado (y requiere C++). (Hemos utilizado un enfoque similar para nuestras necesidades cuando implementamos compatibilidad Unicode.)

El problema fundamental para C es que printf usando puntos suspensivos no es seguro por diseño; necesita determinar el tamaño del argumento adicional a partir de los argumentos conocidos, por lo que no se puede reparar para soportar "lo que sea que tenga". Entonces, a menos que su compilador implemente algunas extensiones propietarias, no tiene suerte.

+2

la 'z' size modidfier es estándar C, pero algunas implementaciones libc están estancadas en 1990 por varias razones (p. ej. Microsoft básicamente abandonó C a favor de C++ y, más recientemente, C#) – Christoph

+3

C99 definió el especificador de tamaño 'z' como el tamaño de un tamaño_t valor, y 't' para ser el tamaño de un valor ptrdiff_t. – swestrup

+0

gracias por los comentarios, lo he incorporado en la respuesta – peterchen

47

Por C89, utilice %lu y convertir el valor a unsigned long:

size_t foo; 
... 
printf("foo = %lu\n", (unsigned long) foo); 

Por C99 y posteriormente, utilizar %zu:

size_t foo; 
... 
printf("foo = %zu\n", foo); 
+5

Considerando 2013, sugiera "Para C99 en adelante" y "Para pre C99:". La mejor respuesta. – chux

+5

No hagas esto. Fallará en Windows de 64 bits donde size_t es de 64 bits y long de 32 bits. – Yttrill

+1

@Yttrill: ¿Cuál es la respuesta para las ventanas de 64 bits, entonces? –

5

Para aquellos hablando de hacer esto en C++ que no lo hace necesariamente admite las extensiones C99, entonces recomiendo encarecidamente el formato boost ::. Esto hace que el tipo size_t cuestión discutible tamaño:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var); 

Dado que no es necesario especificadores tamaño de impulso :: formato, que sólo puede preocuparse por la forma en que desea visualizar el valor.

+3

Probablemente quiera '% u' luego. – GManNickG

-3

En algunas plataformas y para algunos tipos hay especificadores de conversión de printf específicos disponibles, pero a veces hay que recurrir a la conversión a tipos más grandes.

he documentado este problema difícil aquí, con el ejemplo de código: http://www.pixelbeat.org/programming/gcc/int_types/ y actualizarlo periódicamente con información sobre nuevas plataformas y tipos.

+1

Tenga en cuenta que las respuestas de solo vínculo no se recomiendan, por lo que las respuestas deberían ser el punto final de una búsqueda de una solución (frente a otra escala de referencias, que tienden a quedarse obsoletas en el tiempo). Considere agregar una sinopsis independiente aquí, manteniendo el enlace como referencia. – kleopatra

8

Extendiendo la respuesta de Adam Rosenfield para Windows.

He probado este código con tanto en VS2013 Update 4 y VS2015 previsualización:

// test.c 

#include <stdio.h> 
#include <BaseTsd.h> // see the note below 

int main() 
{ 
    size_t x = 1; 
    SSIZE_T y = 2; 
    printf("%zu\n", x); // prints as unsigned decimal 
    printf("%zx\n", x); // prints as hex 
    printf("%zd\n", y); // prints as signed decimal 
    return 0; 
} 

VS2015 genera salidas binarias:

mientras el generado por VS2013 dice:

zu
zx
zd

Nota: ssize_t es una extensión POSIX y SSIZE_T es cosa similar en Windows Data Types, por lo tanto, añadí <BaseTsd.h> referencia.

Adicionalmente, a excepción del seguimiento C99/C11 cabeceras, todas las cabeceras C99 están disponibles en la vista previa VS2015:

C11 - <stdalign.h> 
C11 - <stdatomic.h> 
C11 - <stdnoreturn.h> 
C99 - <tgmath.h> 
C11 - <threads.h> 

Además, <uchar.h> de C11 se incluye ahora en la última vista previa.

Para obtener más información, consulte este old y la lista new para la conformidad estándar.

+0

VS2013 La Actualización 5 produce los mismos resultados que la Actualización 4 le dio. –

0

Como dijo AraK, la interfaz de C++ streams siempre funcionará de manera portátil.

std :: size_t s = 1024; std :: cout < < s; // o cualquier otro tipo de secuencia como stringstream!

Si desea C stdio, no hay una respuesta portátil para ciertos casos de "portátil". Y se pone feo ya que, como ha visto, elegir las banderas de formato incorrecto puede producir una advertencia del compilador o dar un resultado incorrecto.

C99 intentó resolver este problema con formatos inttypes.h como "%" PRIdMAX "\ n". Pero al igual que con "% zu", no todos son compatibles con c99 (como MSVS antes de 2013). Hay archivos "msinttypes.h" flotando para tratar esto.

Si transfiere a un tipo diferente, dependiendo de las banderas, puede obtener una advertencia de compilación para el truncamiento o un cambio de signo. Si elige esta ruta, elija un tipo de tamaño fijo relevante más grande. Uno de unsigned long long y "% llu" o unsigned long "% lu" debería funcionar, pero llu también puede ralentizar las cosas en un mundo de 32 bits como excesivamente grande. (Editar - mi mac emite una advertencia en 64 bits para% llu que no coincide con size_t, aunque% lu,% llu y size_t son todos del mismo tamaño.% Lu y% llu no tienen el mismo tamaño en mi MSVS2012. es posible que necesite emitir + usar un formato que coincida).

De hecho, puede ir con tipos de tamaño fijo, como int64_t. ¡Pero espera! Ahora volvemos a c99/C++ 11 y MSVS anterior falla nuevamente. Además, también tienes yesos (por ejemplo, map.size() no es un tipo de tamaño fijo).

Puede usar un encabezado o biblioteca de un tercero como, por ejemplo, boost. Si aún no está usando uno, puede que no desee inflar su proyecto de esa manera. Si está dispuesto a agregar uno solo por este problema, ¿por qué no usar secuencias de C++ o compilación condicional?

Por lo tanto, tiene que ver con los streams de C++, la compilación condicional, los frameworks de terceros, o algún tipo de dispositivo portátil que funcione para usted.

-3

si desea imprimir el valor de un size_t como una cadena que puede hacer esto:

char text[] = "Lets go fishing in stead of sitting on our but !!"; 
size_t line = 2337200120702199116; 

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */ 
printf("number: %I64d\n",*(size_t*)&text); 
printf("text: %s\n",*(char(*)[])&line); 

resultado es:

número: 2337200120702199116

texto: Le permite ir a pescar en lugar de sentarse en nuestro pero !!

Editar: releer la pregunta debido a los votos Bajar Noté su problema no es% llu o% I64d pero el tipo size_t en diferentes máquinas ven esta pregunta https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/

size_t es unsigned int en un 32 bits máquina y unsigned long long int en 64bit
pero% ll siempre espera una longitud larga sin signo int.

size_t varía en longitud en diferentes sistemas operativos al mismo tiempo% llu es el mismo

+1

¡¿Qué tonterías es esta ?! –

+0

lanzando los primeros 8 bytes de la matriz char a un 64bit largo sin signo a través del puntero size_t e imprímalos como número con printf% I64d no es realmente espectacular, lo sé, por supuesto que no codifiqué para evitar el desbordamiento de tipos pero eso no está en el alcance de la pregunta. – Andre

Cuestiones relacionadas