¿Cómo se puede imprimir una variable size_t de forma portátil utilizando la familia printf?

Tengo una variable de tipo size_t , y quiero imprimir usando printf() . ¿Qué especificador de formato utilizo para imprimirlo de forma portátil?

En una 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 = ; printf( "size = %u\n", x ); warning: format '%u' expects type 'unsigned int', but argument 2 has type 'long unsigned int' 

La advertencia desaparece, como era de esperar, si cambio eso a %lu .

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

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

Use 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 

Parece que varía según el comstackdor que esté utilizando (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 de nuevo firmado, etc.) – pero a partir de cl v19 (en Visual Studio 2015), Microsoft admite %zu (ver esta respuesta a este comentario )

… y, por supuesto, si usa C ++, puede usar cout lugar de lo sugerido por AraK .

Para C89, use %lu y establezca el valor en unsigned long :

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

Para C99 y posterior, use %zu :

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

Ampliando la respuesta de Adam Rosenfield para Windows.

Probé este código con la vista previa de VS2013 Update 4 y VS2015:

 // test.c #include  #include  // 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 tanto, referencia .

Además, a excepción de los siguientes encabezados C99 / C11, todos los encabezados C99 están disponibles en la vista previa VS2015:

 C11 -  C11 -  C11 -  C99 -  C11 -  

Además, C11 ahora se incluye en la última vista previa.

Para obtener más detalles, consulte esta lista antigua y nueva para la conformidad estándar.

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

Para aquellos que hablan de hacer esto en C ++ que no necesariamente soporta las extensiones C99, recomiendo muchísimo el formato boost ::. 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); 

Como no necesita los especificadores de tamaño en el formato boost ::, simplemente puede preocuparse por cómo desea mostrar el valor.

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

Como dijo AraK, la interfaz de transmisión c ++ 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 comstackdor 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 seleccionas un tipo diferente, dependiendo de las banderas, puedes obtener una advertencia de comstackció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).

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 yesos (por ejemplo, map.size () no es un tipo de tamaño fijo).

Puede usar un encabezado o biblioteca de terceros, como 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 comstackción condicional?

Por lo tanto, está acostumbrado a las transmisiones en c ++, la comstackción condicional, los marcos de terceros o algún tipo de dispositivo portátil que funcione para usted.

¿Te avisará si pasas 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 que puede insertar en el literal de cadena de formato, pero no veo ese encabezado en mi comstackdor de Windows C ++, lo que implica que puede no ser multiplataforma.

Si está utilizando C ++ 11, puede convertir size_t en std :: string por std :: to_string, pero no es elegante (cree una cadena de temperatura).

Por ejemplo:

 size_t st = 555; printf("%s", std::to_string(st).c_str()); 

enter image description here

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 , que palabra woulkd en estos dos escenarios, pero tampoco es una opción portátil, y da el valor en hexadecimal.

Alternativamente, use algo de transmisión (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 arreglar para admitir “lo que sea que tenga”. Entonces, a menos que su comstackdor implemente algunas extensiones propietarias, no tiene suerte.

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

He documentado este problema complicado aquí, con 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.

si desea imprimir el valor de un 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); 

resultado es:

Número: 2337200120702199116

texto: Vamos a pescar en lugar de sentarnos en nuestro pero !!

Editar: releyendo la pregunta debido a los votos a la baja, noté que su problema no es% llu o% I64d, sino que el tipo size_t en máquinas diferentes consulta esta pregunta https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/

size_t no tiene firma int en una máquina de 32 bits y unsigned long long int en 64bit
pero% ll siempre espera un int largo largo sin signo.

size_t varía en longitud en diferentes sistemas operativos, mientras que% llu es el mismo