sintaxis - ¿Cómo se puede imprimir una variable size_t usando la familia printf?
printf y scanf en dev c++ (12)
¿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 formato de cadena literal pero no veo ese encabezado en mi compilador de Windows C ++, lo que implica que puede que no sea multiplataforma.
Tengo una variable de tipo size_t
, y quiero imprimirla usando printf()
. ¿Qué especificador de formato utilizo para imprimirlo de forma portátil?
En la máquina de 32 bits, %u
parece correcto. Compilé 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 desaparece, como se esperaba, si la cambio a %lu
.
La pregunta es, ¿cómo puedo escribir el código para que compile sin avisos en máquinas de 32 y 64 bits?
Edición: supongo que una respuesta podría ser "convertir" la variable en un unsigned long
, e imprimir con %lu
. Eso funcionaría en ambos casos. Estoy mirando si hay alguna otra idea.
Ampliando la respuesta de Adam Rosenfield para Windows.
Probé este código con la actualización de VS2013 Update 4 y VS2015:
// 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:
1
1
2
mientras que el generado por VS2013 dice:
zu
zx
zd
Nota: ssize_t
es una extensión POSIX y SSIZE_T
es algo similar en los tipos de datos de Windows , por lo que <BaseTsd.h>
referencia <BaseTsd.h>
.
Además, a excepción de los siguientes encabezados C99 / C11, todos los encabezados C99 están disponibles en la vista previa de VS2015:
C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>
Además, <uchar.h>
C11 ahora se incluye en la última vista previa.
Para obtener más detalles, consulte esta lista old y la new para el cumplimiento estándar.
C99 define "% zd", etc. para eso. (gracias a los comentaristas) No hay un especificador de formato portátil para C ++; podría usar %p
, que es una palabra errónea en estos dos escenarios, pero tampoco es una opción portátil, y da el valor en hexadecimal.
Alternativamente, use alguna transmisión (por ejemplo, cadena de caracteres) o un reemplazo seguro de printf como Boost Format . Entiendo que este consejo es solo de uso limitado (y requiere C ++). (Hemos utilizado un enfoque similar adaptado a nuestras necesidades al implementar el soporte de Unicode).
El problema fundamental para C es que el uso de puntos suspensivos de impresión no es seguro por su diseño: debe determinar el tamaño del argumento adicional a partir de los argumentos conocidos, por lo que no se puede arreglar para admitir "lo que sea que tenga". Entonces, a menos que tu compilador implemente algunas extensiones propietarias, estás fuera de suerte.
Como dijo AraK, la interfaz de flujos de c ++ siempre funcionará de manera portátil.
std :: size_t s = 1024; std :: cout << s; // o cualquier otro tipo de flujo como stringstream!
Si desea C stdio, no hay una respuesta portátil para esto en ciertos casos de "portátil". Y se pone feo ya que, como has visto, elegir las marcas de formato incorrecto puede producir una advertencia del compilador o dar un resultado incorrecto.
C99 intentó resolver este problema con los formatos inttypes.h como "%" PRIdMAX "/ n". Pero al igual que con "% zu", no todos admiten c99 (como MSVS antes de 2013). Hay archivos "msinttypes.h" flotando alrededor para lidiar con esto.
Si realiza la conversión a un tipo diferente, dependiendo de los indicadores, puede recibir una advertencia del compilador para el truncamiento o un cambio de signo. Si vas por esta ruta, elige un tipo de tamaño fijo relevante más grande. Debería funcionar uno de los largos sin signo largos y "% llu" o los largos sin firmar "% lu", pero también puede ralentizar las cosas en un mundo de 32 bits como demasiado grande. (Editar: mi Mac emite una advertencia en 64 bits para que% llu no coincida con size_t, aunque% lu,% llu y size_t sean del mismo tamaño. Y% lu y% llu no son del mismo tamaño en mi MSVS2012. puede que necesites lanzar + usar un formato que coincida.)
Para el caso, 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 tiene conversiones (por ejemplo, map.size () no es un tipo de tamaño fijo).
Puede utilizar un encabezado de terceros o una biblioteca como boost. Si aún no está utilizando uno, es posible que no desee inflar su proyecto de esa manera. Si está dispuesto a agregar uno solo para este problema, ¿por qué no usa las secuencias de c ++ o la compilación condicional?
Por lo tanto, se reduce a las secuencias de C ++, la compilación condicional, los marcos de terceros o algún tipo de dispositivo portátil que funcione para usted.
En algunas plataformas y para algunos tipos hay especificadores de conversión específicos de printf disponibles, pero a veces uno tiene que recurrir a la conversión de tipos más grandes.
He documentado este problema complicado aquí, con el código de ejemplo: http://www.pixelbeat.org/programming/gcc/int_types/ y lo actualizo periódicamente con información sobre nuevas plataformas y tipos.
Para C89, use %lu
y convierta el valor a un unsigned long
:
size_t foo;
...
printf("foo = %lu/n", (unsigned long) foo);
Para C99 y versiones posteriores, use %zu
:
size_t foo;
...
printf("foo = %zu/n", foo);
Para aquellos que hablan de hacer esto en C ++, que no necesariamente admite las extensiones C99, entonces recomiendo de todo corazón boost :: format. Esto hace que la pregunta de tamaño de tipo size_t sea discutible:
std::cout << boost::format("Sizeof(Var) is %d/n") % sizeof(Var);
Ya que no necesita especificadores de tamaño en formato boost ::, solo puede preocuparse sobre cómo desea mostrar el valor.
Parece que varía según el compilador que estés usando (blech):
- gnu dice
%zu
(o%zx
, o%zd
pero eso lo muestra como si estuviera firmado, etc.) - Microsoft dice
%Iu
(o%Ix
, o%Id
pero nuevamente está firmado, etc.), pero a partir de cl v19 (en Visual Studio 2015), Microsoft admite%zu
(vea esta respuesta a este comentario )
... y, por supuesto, si está utilizando C ++, puede usar cout
lugar de lo sugerido por AraK .
Si desea imprimir el valor de size_t como una cadena, 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);
el resultado es:
Número: 2337200120702199116
texto: ¡Vamos a pescar en lugar de sentarnos en nuestro pero!
Edición: al volver a leer la pregunta debido a los votos a la baja, noté que su problema no es% llu o% I64d, pero el tipo size_t en diferentes máquinas ve esta pregunta https://.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/
size_t es un unsigned int en una máquina de 32 bits y un unsigned long long int en 64 bits
pero% ll siempre espera un int largo y largo sin firmar.
size_t varía en longitud en diferentes sistemas operativos mientras que% llu es el mismo
Usa el modificador z
:
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
printf("size = %zu/n", sizeof(thing) );
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!