Corregir el especificador de formato para imprimir el 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?

La respuesta más simple, suponiendo que no te molesten los caprichos y las variaciones en el 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 debe ser un puntero a void . El valor del puntero se convierte en una secuencia de caracteres de impresión, de una manera definida por la implementación.

(En C11 – ISO / IEC 9899: 2011 – la información se encuentra en §7.21.6.1 ¶8.)

En algunas plataformas, eso incluirá un 0x líder y en otros 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 sé de ninguna implementación donde no lo es.

Está algo abierto para debatir si debe convertir explícitamente los punteros con un molde (void *) . Está siendo explícito, lo que generalmente es bueno (así que es lo que hago), y el estándar dice ‘el argumento será un puntero al void ‘. En la mayoría de las máquinas, se saldría con la omisión de un lanzamiento explícito. Sin embargo, importaría en una máquina donde la representación de bits de una dirección de caracteres char * para una ubicación de memoria dada es diferente de la dirección de ‘ cualquier otro apuntador ‘ para la misma ubicación de memoria. Esta sería una máquina dirigida por palabra, en lugar de 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 una de esas (ICL Perq).

Si no está contento con el comportamiento definido por la implementación de %p , entonces use C99 y uintptr_t en uintptr_t 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úscula para que el número sea uniformemente de la misma altura y el salto característico al inicio de 0xA1B2CDEF aparezca así, no como 0xa1b2cdef que también baja y baja a lo largo del número. Tu elección, sin embargo, dentro de límites muy amplios. La (uintptr_t) es recomendada inequívocamente por GCC cuando puede leer la cadena de formato en tiempo de comstackción. Creo que es correcto solicitar el elenco, aunque estoy seguro de que hay algunos que ignorarían la advertencia y saldrían con la suya la mayor parte del tiempo.


Kerrek pregunta en los comentarios:

Estoy un poco confundido acerca de las promociones estándar y los argumentos variados. ¿Todos los punteros se promocionan de forma estándar a void *? De lo contrario, si int* fueran, por ejemplo, dos bytes y void* fueran 4 bytes, entonces sería claramente un error leer cuatro bytes del argumento, ¿no?

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 * e int * no pueden ser de diferentes tamaños. Sin embargo, lo que creo que es la sección relevante de la norma C99 no es tan enfática (aunque no sé de una implementación donde lo que sugerí es verdadero 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 deben tener los mismos requisitos de representación y alineación entre sí. Todos los punteros a tipos de unión tendrán los mismos requisitos de representación y alineación entre 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 pretenden implicar intercambiabilidad como argumentos para funciones, valores de retorno de funciones y miembros de uniones.

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

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

El estándar C definitivamente no requiere que los punteros a las funciones sean del mismo tamaño que los punteros a objetos. Eso fue 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 función de 32 bits, o viceversa. Esta es la razón por la cual el estándar C no exige que los punteros a las funciones se puedan convertir a punteros a objetos y viceversa.

Afortunadamente (para los progtwigdores que apuntan a POSIX), POSIX entra en la brecha y exige que los punteros de función y los punteros de datos sean del mismo tamaño:

§2.12.3 Tipos de punteros

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

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

Por lo tanto, parece que los moldes explícitos a void * son muy recomendables para una máxima confiabilidad en el código al pasar un puntero a una función variadica como printf() . En sistemas POSIX, es seguro lanzar un puntero a un puntero vacío para imprimir. En otros sistemas, no es necesariamente seguro hacer eso, ni es seguro pasar punteros que no sean void * sin un yeso.

p es el especificador de conversión para imprimir punteros. Utilizar esta.

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

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

Use %p , para “puntero”, y no use nada más *. El estándar no le garantiza que pueda tratar un puntero como cualquier tipo particular de entero, por lo que obtendrá un comportamiento indefinido con los formatos integrales. (Por ejemplo, %u espera un unsigned int , pero ¿qué void* si void* tiene un tamaño diferente o un requisito de alineación distinto de unsigned int ?)

*) [¡Ver la respuesta Jonathan!] Alternativamente a %p , puede usar macros específicas de puntero de , agregado en C99.

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

 printf("x lives at %p.\n", (void*)&x); 

Como alternativa a las otras (muy buenas) respuestas, puede convertir a uintptr_t o intptr_t (de stdint.h / inttypes.h ) y usar los especificadores de conversión enteros correspondientes. Esto permitiría una mayor flexibilidad en el formateo del puntero, pero estrictamente hablando, no se requiere una implementación para proporcionar estos typedefs.