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

8 minutos de lectura

avatar de usuario
Arun

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

En una 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 cambio eso a %lu.

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

Editar: como solución alternativa, supongo que una respuesta podría ser “convertir” la variable en un número entero que sea lo suficientemente grande, digamos unsigned longe imprimir usando %lu. Eso funcionaría en ambos casos. Estoy mirando si hay alguna otra idea.

  • echando a unsigned long es la mejor opción si su implementación de libc no es compatible con z modificador; el estándar C99 recomienda size_t no tener un rango de conversión de entero mayor que longentonces estás razonablemente seguro

    – Cristóbal

    26 de marzo de 2010 a las 16:10

  • posible duplicado de los especificadores de formato size_t independientes de la plataforma en c?

    – maxschlepzig

    1 de marzo de 2014 a las 13:29

  • ¿Cuál es la forma correcta de usar printf para imprimir un size_t?

    – phuclv

    16 de agosto de 2015 a las 9:19

  • En la plataforma Windows, size_t puede ser más grande que largo. Por razones de compatibilidad, long siempre es de 32 bits, pero size_t puede ser de 64 bits. Por lo tanto, la conversión a largo sin firmar puede perder la mitad de los bits. Lo siento 🙂

    –Bruce Dawson

    07/08/2017 a las 20:48

  • Posible duplicado de ¿Cuál es la forma correcta de usar printf para imprimir un size_t?

    – phuclv

    30 de enero de 2019 a las 2:06

Utilizar 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

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

    – Avakar

    26 de marzo de 2010 a las 16:20


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

    – Cristóbal

    26 de marzo de 2010 a las 16:28

  • @Christoph: Tampoco está en el último borrador, n3035.

    – GManNickG

    26 de marzo de 2010 a las 16:48

  • @avakar @Adam Rosenfield @Christoph @GMan: Sin embargo, en n3035 §1.2 Referencias normativas, solo se hace referencia al estándar C99, y §17.6.1.2/3 de los mismos estados “Se proporcionan las instalaciones de la biblioteca estándar C”. Yo interpretaría esto en el sentido de que, a menos que se especifique lo contrario, todo en la biblioteca estándar de C99 es parte de la biblioteca estándar de C++0x, incluidos los especificadores de formato adicionales en C99.

    –James McNellis

    28 de marzo de 2010 a las 2:49


  • @ArunSaha: es una característica de solo C99, no de C++. Si quieres que se compile con -pedantic, deberá obtener un compilador compatible con el borrador de C++ 1x (muy poco probable), o deberá mover su código a un archivo compilado como C99. De lo contrario, su única opción es convertir sus variables a unsigned long long y use %llu para ser máximamente portátil.

    – Adam Rosenfield

    13 de abril de 2010 a las 1:52

avatar de usuario
Juan Bode

Para C89, use %lu y arrojar el valor a unsigned long:

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

Para C99 y posteriores, use %zu:

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

  • Teniendo en cuenta 2013, sugiera “Para C99 y posteriores” y “Para pre C99:”. La mejor respuesta.

    – chux – Reincorporar a Monica

    10 de agosto de 2013 a las 5:52

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

    – Ytrill

    10 de octubre de 2015 a las 6:08

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

    – Juan Bode

    16 de diciembre de 2015 a las 12:28

  • O: podrías lanzar a un uint64_t y luego usa el PRIu64 macro de inttypes.h, que contiene el especificador de formato.

    – James Ko

    02/09/2016 a las 16:50

  • @JamesKo ¿cuál es el punto en eso? uint64_t es C99, así que si está disponible, también lo está "%zu" (que es la forma correcta de hacerlo).

    –Craig Barnes

    5 ago. 2018 a las 14:00

avatar de usuario
keith thompson

En cualquier implementación C razonablemente moderna, "%zu" es la forma correcta de imprimir un valor de tipo size_t:

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

Él "%zu" El especificador de formato se agregó en el estándar ISO C de 1999 (y se adoptó en el estándar ISO C++ de 2011). Si no necesita preocuparse por las implementaciones anteriores, puede dejar de leer ahora.

Si su código necesita ser portátil para implementaciones anteriores a C99, puede convertir el valor a unsigned long y use "%lu":

printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));

Eso no es portátil para C99 o posterior, porque C99 introdujo long long y unsigned long longy por lo tanto la posibilidad de que size_t es más ancho que unsigned long.

Resiste la tentación de usar "%lu" o "%llu" sin el elenco. El tipo utilizado para implementar size_t está definido por la implementación y, si los tipos no coinciden, el comportamiento no está definido. Algo como printf("%lu\n", sizeof (int)); podría “funcionar”, pero no es del todo portátil.

En principio, lo siguiente debería cubrir todos los casos posibles:

#if __STDC_VERSION__ < 199901L
    printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
#else
    printf("sizeof (int) = %zu\n", sizeof (int));
#endif

En la práctica, puede que no siempre funcione correctamente. __STD_VERSION__ >= 199901L debería garantizar que "%zu" es compatible, pero no todas las implementaciones son necesariamente correctas, especialmente porque __STD_VERSION__ es establecido por el compilador y "%zu" es implementado por la biblioteca de tiempo de ejecución. Por ejemplo, una implementación con parcial El soporte C99 podría implementar long long y hacer size_t una definición de tipo para unsigned long longpero no apoyo "%zu". (Tal implementación probablemente no definiría __STDC_VERSION__.)

Se ha señalado que la implementación de Microsoft puede tener 32 bits unsigned long y 64 bits size_t. Microsoft admite "%zu", pero ese soporte se agregó relativamente tarde. Por otra parte, lanzar a unsigned long será un problema sólo si el particular size_t el valor pasa a exceder ULONG_MAXlo que es poco probable que suceda en la práctica.

Si puede asumir implementaciones razonablemente modernas, simplemente use "%zu". Si necesita permitir implementaciones más antiguas, aquí hay un programa absurdamente portátil que se adapta a varias configuraciones:

#include <stdio.h>
#include <limits.h>
int main(void) {
    const size_t size = -1; /* largest value of type size_t */
#if __STDC_VERSION__ < 199901L
    if (size > ULONG_MAX) {
        printf("size is too big to print\n");
    }
    else {
        printf("old: size = %lu\n", (unsigned long)size);
    }
#else
    printf("new: size = %zu\n", size);
#endif
    return 0;
}

Una implementación que imprime “el tamaño es demasiado grande para imprimir” (x86_64-w64-mingw32-gcc.exe -std=c90 en Windows/Cygwin) en realidad admite unsigned long long como una extensión sobre C90, por lo que es posible que pueda aprovechar eso, pero puedo imaginar una implementación anterior a C99 que admita unsigned long long pero no soporta "%llu". Y esa implementación apoya "%zu" de todas formas.

En mi experiencia, solo he querido imprimir size_t valores en código desechable rápido cuando estoy explorando una implementación en lugar de en código de producción. En ese tipo de contexto, probablemente sea suficiente hacer lo que sea que funcione.

(La pregunta es sobre C, pero lo mencionaré en C++ std::cout << sizeof (int) funcionará correctamente en cualquier versión del idioma).

avatar de usuario
TJ Crowder

Parece que varía según el compilador que esté usando (blech):

…y por supuesto, si está usando C++, puede usar cout en cambio, como lo sugiere AraK.

avatar de usuario
ganso nate

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

avatar de usuario
swestrup

Para aquellos que hablan de hacer esto en C ++, que no necesariamente es compatible con las extensiones C99, recomiendo encarecidamente 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);

Dado que no necesita especificadores de tamaño en boost::format, solo puede preocuparse por cómo desea mostrar el valor.

Como dijo AraK, la interfaz de flujos de c ++ siempre funcionará de forma portátil.

estándar::tamaño_t s = 1024; estándar::cout << s; // o cualquier otro tipo de flujo como stringstream!

Si desea C stdio, no existe una respuesta portátil para ciertos casos de “portátil”. Y se pone feo ya que, como ha visto, elegir las banderas de formato incorrectas puede generar 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 admiten c99 (como MSVS antes de 2013). Hay archivos “msinttypes.h” flotando para lidiar con esto.

Si convierte a un tipo diferente, dependiendo de las banderas, puede recibir una advertencia del compilador por truncamiento o cambio de signo. Si sigue 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 por ser 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 tienen el mismo tamaño. Y %lu y %llu no tienen el mismo tamaño en mi MSVS2012. Entonces es posible que deba emitir + usar un formato que coincida).

De hecho, puede optar por tipos de tamaño fijo, como int64_t. ¡Pero espera! Ahora volvimos a c99/c++11, y el MSVS anterior falla nuevamente. ¡Además, también tiene moldes (por ejemplo, map.size() no es un tipo de tamaño fijo)!

Puede usar un encabezado o una biblioteca de terceros, como boost. Si aún no está usando 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 usar flujos de c ++ o compilación condicional?

Por lo tanto, debe usar flujos de c ++, compilación condicional, marcos de trabajo de terceros o algo portátil que funcione para usted.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad