¿Cuál es el uso correcto de printf para mostrar los punteros rellenados con 0s?

En C, me gustaría usar printf para mostrar los punteros, y para que se alineen correctamente, me gustaría rellenarlos con 0s.

Mi conjetura fue que la forma correcta de hacer esto fue:

  printf ("% 016p", ptr); 

Esto funciona, pero este gcc se queja con el siguiente mensaje:

  advertencia: el indicador '0' se usa con el formato '% p' ​​gnu_printf 

He buscado un poco en Google para ello, y el siguiente hilo es sobre el mismo tema, pero realmente no da una solución.

http://gcc.gnu.org/ml/gcc-bugs/2003-05/msg00484.html

Al leerlo, parece que la razón por la cual gcc se queja es que la syntax que sugerí no está definida en C99. Pero parece que no puedo encontrar otra manera de hacer lo mismo en una forma estándar aprobada.

Así que aquí está la doble pregunta:

  • ¿Tengo razón al entender que este comportamiento no está definido por el estándar C99?
  • Si es así, ¿hay una forma estándar aprobada y portátil de hacer esto?

#include

#include

printf("%016" PRIxPTR "\n", (uintptr_t)ptr);

pero no imprimirá el puntero en la forma definida de implementación (dice DEAD:BEEF para el modo segmentado 8086).

Utilizar:

 #include  printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer); 

O use otra variante de las macros de ese encabezado.

También tenga en cuenta que algunas implementaciones de printf() imprimen un ‘ 0x ‘ delante del puntero; otros no (y ambos son correctos según el estándar C).

La única forma portátil de imprimir valores de puntero es usar el especificador "%p" , que produce resultados definidos por la implementación. (La conversión a uintptr_t y el uso de PRIxPTR es probable que funcione, pero (a) uintptr_t se introdujo en C99, por lo que es posible que algunos comstackdores no lo admitan, y (b) no está garantizado que exista incluso en C99 (puede que no haya un tipo entero) lo suficientemente grande como para mantener todos los valores de puntero posibles.

Entonces, use "%p" con sprintf() (o snprintf() , si lo tiene) para imprimir en una cadena, y luego imprima el relleno de la cadena con los espacios en blanco iniciales.

Un problema es que no hay una manera realmente buena de determinar la longitud máxima de la cadena producida por "%p" .

Esto es ciertamente inconveniente (aunque, por supuesto, puedes envolverlo en una función). Si no necesita un 100% de portabilidad, nunca he usado un sistema donde el puntero unsigned long y la impresión del resultado utilizando "0x%16lx" no funcionaría. [ ACTUALIZACIÓN : Sí, tengo. Windows de 64 bits tiene punteros de 64 bits y 32 bits unsigned long .] (O puede usar "0x%*lx" y calcular el ancho, probablemente algo como sizeof (void*) * 2 Si es realmente paranoico , puede tener en cuenta los sistemas donde CHAR_BIT > 8 , por lo que obtendrá más de 2 dígitos hexadecimales por byte.)

Esta respuesta es similar a la dada anteriormente en https://stackoverflow.com/a/1255185/1905491 pero también tiene en cuenta los posibles anchos (como se describe en https://stackoverflow.com/a/6904396/1905491) . No reconocí hasta que mi respuesta fue rendida debajo de ella;). El siguiente recorte imprimirá los punteros como 8 caracteres hexadecimales pasados ​​en máquinas sane * en las que se pueden representar con 32 bits, 16 en 64b y 4 en sistemas 16b.

 #include  #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2)) printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer); 

Tenga en cuenta el uso del carácter de asterisco para obtener el ancho en el siguiente argumento, que se encuentra en C99 (probablemente antes?), Pero que rara vez se ve “en la naturaleza”. Esto es mucho mejor que usar la conversión p porque esta última está definida por la implementación.

* El estándar permite que uintptr_t sea ​​más grande que el mínimo, pero asumo que no hay ninguna implementación que no use el mínimo.

Es fácil de resolver si lanzas el puntero a un tipo largo. El problema es que esto no funcionará en todas las plataformas, ya que asume que un puntero puede caber dentro de un largo, y que un puntero puede mostrarse en 16 caracteres (64 bits). Sin embargo, esto es cierto en la mayoría de las plataformas.

por lo que se convertiría en:

 printf("%016lx", (unsigned long) ptr); 

Como su enlace ya sugiere, el comportamiento no está definido. No creo que haya una forma portátil de hacerlo ya que% p depende de la plataforma en la que estés trabajando. Por supuesto, podría simplemente lanzar el puntero a un int y mostrarlo en hexadecimal:

 printf("0x%016lx", (unsigned long)ptr); 

Tal vez esto sea interesante (desde una máquina de Windows de 32 bits, usando mingw):

 rjeq@RJEQXPD /u $ cat test.c #include  int main() { char c; printf("p: %016p\n", &c); printf("x: %016llx\n", (unsigned long long) (unsigned long) &c); return 0; } rjeq@RJEQXPD /u $ gcc -Wall -o test test.c test.c: In function `main': test.c:7: warning: `0' flag used with `%p' printf format rjeq@RJEQXPD /u $ ./test.exe p: 0022FF77 x: 000000000022ff77 

Como puede ver, la versión% p se rellena con ceros del tamaño de un puntero, y luego los espacios al tamaño especificado, mientras que el uso de% xy las conversiones (los moldes y el especificador de formato son altamente imposibles de transportar) solo utiliza ceros.

Tenga en cuenta que si el puntero se imprime con el prefijo “0x”, deberá sustituir “% 018p” para compensar.

Normalmente uso% x para mostrar los punteros. Supongo que no es portátil para sistemas de 64 bits, pero funciona bien para 32 bitters.

Me interesará ver qué respuestas hay para las soluciones portátiles , ya que la representación de punteros no es exactamente portátil.