2012-01-29 17 views

Respuesta

133

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

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 manera definida por la implementación .

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

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

Es un poco abierto 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 debe 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 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 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á satisfecho con el comportamiento definido por la implementación de %p, a continuación, utilizar C99 <inttypes.h> y uintptr_t lugar:

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

Esto le permite ajustar con precisión la representación a tu gusto. Elegí tener los dígitos hexadecimales en mayúscula para que el número sea uniformemente de la misma altura y aparezca la inclinación característica al inicio de 0xA1B2CDEF, no como 0xa1b2cdef, que también se inclina hacia arriba y hacia abajo a lo largo del número. Tu elección, sin embargo, dentro de límites muy amplios. La conversión de (uintptr_t) es recomendada inequívocamente por GCC cuando puede leer la cadena de formato en tiempo de compilació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 argumentos variadic. ¿Todos los punteros se promocionan de forma 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, ¿no?

que estaba bajo la ilusión de que el estándar de C dice que todos los punteros a objetos deben ser del mismo tamaño, por lo void * y int * no pueden ser de diferentes tamaños. Sin embargo, lo que creo que es la sección pertinente de la norma C99 no es tan enfático (aunque no sé de una aplicación donde lo que he sugerido es cierto en realidad es falsa):

§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) De forma similar, los punteros a versiones calificadas o no calificadas de tipos compatibles deben 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 implican la intercambiabilidad como argumentos para funciones, valores devueltos de funciones y miembros de uniones.

(C11 dice exactamente lo mismo en la sección §6.2.5, ¶28, y 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 señaladas por los punteros pueden tener requisitos de alineación diferentes. 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 (es decir, 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 programadores y dirigidos POSIX), POSIX pasos en la brecha y hace el mandato de que los punteros de función y los punteros de datos son del mismo tamaño:

§2.12.3 Pointer Types

Todos los tipos de puntero de función tendrá 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 de void * resultante de dicha conversión puede convertirse de nuevo 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 lo requiere, pero se requiere para la conformidad con POSIX.

Por lo tanto, parece que conversiones explícitas a void * son muy aconsejable para la máxima fiabilidad en el código cuando se pasa un puntero a una función variadic 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 necesariamente seguro pasar punteros que no sean void * sin un molde.

+3

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 *' fuera, por ejemplo, dos bytes, y 'void *' fueran 4 bytes, entonces sería claramente un error leer cuatro bytes del argumento, ¿no? –

+0

Tenga en cuenta que una actualización de POSIX (POSIX 2013) ha eliminado la sección 2.12.3, moviendo la mayoría de los requisitos a ['dlsym()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym .html) en su lugar. Algún día escribiré el cambio ... pero 'un día' no es 'hoy'. –

+0

¿Esta respuesta también se aplica a los punteros a las funciones? ¿Pueden convertirse a 'void *'? Hmm, veo tu comentario [aquí] (http://stackoverflow.com/questions/9053658/correct-format-specifier-to-print-pointer-address#comment11360489_9053677). Como solo se necesita una conversión de un wat (puntero de función a 'void *'), ¿funciona entonces? – chux

28

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

int a = 42; 

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

Recuerde que la omisión de la fundición es un comportamiento indefinido y que la impresión con p indicador de conversión se realiza de una manera definida por la implementación.

+0

Perdón, ¿por qué omitir el elenco es un "comportamiento indefinido"? ¿Esta dirección de la materia de qué variable es, si todo lo que necesita es la dirección, no el valor? – valdo

+8

@valdo porque C lo dice (C99, 7.19.6.1p8) "p El argumento debe ser un puntero a void". – ouah

+9

@valdo: no es necesariamente el caso de que todos los punteros tengan el mismo tamaño/representación. – caf

21

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 lo que si void* tiene un tamaño diferente o requisito de alineación de unsigned int?)

*) [Ver bien la respuesta de Jonathan!] Como alternativa a %p, le pueden los utilizar macros-puntero específica de <inttypes.h>, agregado en C99.

Todos los punteros a objetos son convertir implícitamente a void* en C, pero con el fin de pasar el puntero como un argumento variadic, hay que echarlo de forma explícita (ya que los punteros a objetos arbitrarios son solamente convertibles, pero no idéntica a punteros void):

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

Todos los punteros * object * son convertibles a 'void *' (aunque para 'printf()' técnicamente se necesita el molde explícito, ya que es una función variadica). Los punteros de función no son necesariamente convertibles en 'void *'. – caf

+0

@caf: ¡Oh, no sabía sobre los argumentos variados, corregidos! ¡Gracias! –

+2

El estándar C no requiere que los punteros a las funciones sean convertibles en 'void *' y regresen al puntero de la función sin pérdida; Afortunadamente, sin embargo, POSIX lo exige explícitamente (teniendo en cuenta que no es parte del estándar C). Entonces, en la práctica, puedes salirte con la tuya (convirtiendo 'void (* function) (void)' to 'void *' y volviendo a 'void (* function) (void)'), pero estrictamente no es obligatorio por el estándar C –

8

Como alternativa a los otros (muy bueno) respuestas, en que pudieras jugar a uintptr_t o intptr_t (de stdint.h/inttypes.h) y el uso de los indicadores de conversión entero correspondiente. Esto permitiría una mayor flexibilidad en el formateo del puntero, pero estrictamente hablando, no se requiere una implementación para proporcionar estos typedefs.

+0

Bonito, no sabía nada de 'uintptr_t'. – cnicutar

+0

considere '#include int main (void) {int p = 9; int * m = & s; printf ("% u", m); } '__ ¿Es un comportamiento indefinido imprimir la dirección de la variable usando el especificador de formato'% u'? __ La dirección de la variable en la mayoría de los casos es positiva, ¿puedo usar '% u' en lugar de'% p'? – Destructor

+0

@Destructor: No, '% u' es un formato para el tipo' unsigned int' y no se puede usar con un argumento de puntero a 'printf'. –

Cuestiones relacionadas