Especificador de formato correcto para imprimir puntero (dirección)?


¿Qué especificador de formato debo usar para imprimir la dirección de una variable? Estoy confundido entre el lote de abajo.

% u-entero sin signo

% x-valor hexadecimal

% p-puntero vacío

¿Cuál sería el formato óptimo para imprimir una dirección?

Author: rsp, 2012-01-29

4 answers

La respuesta más simple, suponiendo que no le importen los caprichos y variaciones de formato entre diferentes plataformas, es la notación estándar %p.

El estándar C99 (ISO / IEC 9899: 1999) dice en §7.19.6.1 ¶8:

p El argumento será un puntero a void. El valor del puntero es convertido a una secuencia de caracteres de impresión, en una implementación definida manera.

(En C11-ISO / IEC 9899:2011 - la información está en §7.21.6.1 ¶8.)

En algunas plataformas, eso incluirá un encabezado 0x y en otras no, y las letras podrían estar en minúsculas o mayúsculas, y el estándar C ni siquiera define que será una salida hexadecimal, aunque no sé de ninguna implementación donde no lo sea.

Está abierto a debate si debe convertir explícitamente los punteros con un molde (void *). Es ser explícito, que suele ser bueno (por lo que es lo que hago), y el estándar dice ' el argumento ser un puntero a void'. En la mayoría de las máquinas, te saldrías con la tuya omitiendo un reparto explícito. Sin embargo, importaría en una máquina donde la representación de bits de una dirección char * para una ubicación de memoria dada es diferente de la dirección 'cualquier otro puntero' para la misma ubicación de memoria. Esta sería una máquina dirigida por palabra, en lugar de por byte. Estas máquinas no son comunes (probablemente no están disponibles) en estos días, pero la primera máquina en la que trabajé después de la universidad fue uno de estos (ICL Perq).

Si no está satisfecho con el comportamiento definido por la implementación de %p, use C99 <inttypes.h> y uintptr_t en su lugar:

printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);

Esto le permite ajustar la representación a su gusto. Elegí tener los dígitos hexadecimales en mayúsculas para que el número sea uniformemente de la misma altura y la caída característica al comienzo de 0xA1B2CDEF aparece así, no como 0xa1b2cdef que se sumerge hacia arriba y hacia abajo a lo largo del número también. Su elección, sin embargo, dentro de límites muy amplios. El cast (uintptr_t) es inequívocamente recomendado por GCC cuando puede leer la cadena de formato en tiempo de compilación. Creo que es correcto solicitar el reparto, aunque estoy seguro de que hay algunos que ignorarían la advertencia y se saldrían con la suya la mayor parte del tiempo.


Kerrek pregunta en los comentarios:

Estoy un poco confundido sobre las promociones estándar y los argumentos variádicos. ¿Todos los punteros son promovidos estándar a void*? De lo contrario, si int* fueran, digamos, dos bytes, y void* fueran 4 bytes, entonces sería claramente un error leer cuatro bytes del argumento, non?

Estaba bajo la ilusión de que el estándar C dice que todos los punteros de objeto deben ser del mismo tamaño, por lo que void * y int * no pueden ser de diferentes tamaños. Sin embargo, lo que creo que es la sección relevante del estándar C99 no es tan enfática (aunque no conozco una implementación donde lo que sugerí es cierto es en realidad falso):

§6.2.5 Tipos

¶26 Un puntero a void tendrá los mismos requisitos de representación y alineación que un puntero a un tipo de carácter.39) Del mismo modo, los punteros a versiones calificadas o no calificadas de tipos compatibles deberán tener los mismos requisitos de representación y alineación. Todos los punteros a tipos de estructura deberán tener los mismos requisitos de representación y alineación que los demás. Todos los punteros a tipos de unión tendrán los mismos requisitos de representación y alineación que los demás. Los punteros a otros tipos no necesitan tener los mismos requisitos de representación o alineación.

39) Los mismos requisitos de representación y alineación están destinados a implicar intercambiabilidad como argumentos a funciones, valores de retorno de funciones y miembros de uniones.

(C11 dice exactamente lo mismo en la sección §6.2.5, ¶28, y nota 48.)

Por lo tanto, todos los punteros a estructuras deben ser del mismo tamaño que los demás, y deben compartir los mismos requisitos de alineación, a pesar de que el las estructuras a las que apuntan los punteros pueden tener diferentes requisitos de alineación. Del mismo modo para los sindicatos. Los punteros de caracteres y los punteros de vacío deben tener los mismos requisitos de tamaño y alineación. Los punteros a variaciones en int (que significa unsigned int y signed int) deben tener los mismos requisitos de tamaño y alineación que los demás; de manera similar para otros tipos. Pero el estándar C no dice formalmente que sizeof(int *) == sizeof(void *). Bueno, así que es bueno para hacerte inspeccionar tus suposiciones.

El estándar C definitivamente no requiere que los punteros de función sean del mismo tamaño que los punteros de objeto. Eso era necesario para no romper los diferentes modelos de memoria en sistemas similares a DOS. Allí podría tener punteros de datos de 16 bits pero punteros de funciones de 32 bits, o viceversa. Esta es la razón por la que el estándar C no exige que los punteros de función se puedan convertir en punteros de objeto y viceversa.

Afortunadamente (para los programadores que se dirigen a POSIX), POSIX entra en la brecha y ordena esa función los punteros y los punteros de datos tienen el mismo tamaño:

§2.12.3 Tipos de puntero

Todos los tipos de puntero de función tendrán la misma representación que el puntero de tipo a void. La conversión de un puntero de función a void * no alterará la representación. Un valor void * resultante de tal conversión se puede convertir de nuevo al tipo de puntero de función original, utilizando un cast explícito, sin pérdida de información.

Nota: El estándar ISO C no requiere esto, pero se requiere para la conformidad POSIX.

Por lo tanto, parece que los casts explícitos a void * son muy recomendables para la máxima fiabilidad en el código cuando se pasa un puntero a una función variádica como printf(). En los sistemas POSIX, es seguro enviar un puntero de función a un puntero vacío para imprimir. En otros sistemas, no es necesariamente seguro hacer eso, ni es necesariamente seguro pasar punteros que no sean void * sin un cast.

 155
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-06 16:12:57

p es el especificador de conversión para imprimir punteros. Usa esto.

int a = 42;

printf("%p\n", (void *) &a);

Recuerde que omitir el cast es un comportamiento indefinido y que la impresión con p especificador de conversión se realiza de una manera definida por la implementación.

 33
Author: ouah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-29 13:51:18

Use %p, para "puntero", y no use nada más*. No está garantizado por el estándar que se le permite tratar un puntero como cualquier tipo particular de entero, por lo que realmente obtendría un comportamiento indefinido con los formatos integrales. (Por ejemplo, %u espera un unsigned int, pero ¿qué pasa si void* tiene un tamaño o requisito de alineación diferente a unsigned int?)

*) [Ver la buena respuesta de Jonathan!] Alternativamente a %p, puede utilizar macros específicas de puntero de <inttypes.h>, añadido en C99.

Todos los punteros de objeto son implícitamente convertibles a void* en C, pero para pasar el puntero como un argumento variádico, debe lanzarlo explícitamente (ya que los punteros de objeto arbitrarios solo son convertibles, pero no idénticos a los punteros vacíos):

printf("x lives at %p.\n", (void*)&x);
 23
Author: Kerrek SB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-29 17:29:03

Como alternativa a las otras (muy buenas) respuestas, puedes lanzar a uintptr_t o intptr_t (desde stdint.h/inttypes.h) y utilice los especificadores de conversión de enteros correspondientes. Esto permitiría una mayor flexibilidad en la forma en que se formatea el puntero, pero estrictamente hablando no se requiere una implementación para proporcionar estos tipos.

 8
Author: R..,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-29 15:49:54