Respuestas:
La respuesta más simple, suponiendo que no le importen los caprichos y las variaciones de formato entre las diferentes plataformas, es la %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 avoid
. 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 inicio 0x
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 Sin implementación donde no está.
Está algo abierto a debatir si debe convertir explícitamente los punteros con un (void *)
elenco. Está siendo explícito, lo que generalmente es 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, se saldría con la suya omitiendo un reparto explícito. Sin embargo, sería importante en una máquina donde la representación de bits de una char *
dirección para una ubicación de memoria dada es diferente de la dirección de ' cualquier otra cosa ' para la misma ubicación de memoria. Esta sería una máquina con dirección de palabra, en lugar de con dirección de byte. Estas máquinas no son comunes (probablemente no disponibles) en estos días, pero la primera máquina en la que trabajé después de la universidad fue una de ellas (ICL Perq).
Si no está satisfecho con el comportamiento definido por la implementación de %p
, use C99 <inttypes.h>
y en su 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úsculas para que el número sea uniformemente de la misma altura y el descenso característico al comienzo de 0xA1B2CDEF
aparece de esta manera, no como el 0xa1b2cdef
que sube y baja a lo largo del número también. Sin embargo, su elección, dentro de límites muy amplios. El (uintptr_t)
reparto está inequívocamente recomendado por GCC cuando se puede leer el formato de cadena en tiempo de compilación. Creo que es correcto solicitar el reparto, aunque estoy seguro de que algunos 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 variados. ¿Todos los punteros son promovidos a nulo *? De lo contrario, si
int*
fueran, digamos, dos bytes yvoid*
fueran 4 bytes, entonces claramente sería un error leer cuatro bytes del argumento, ¿no?
Yo tenía la ilusión de que el estándar de C dice que todos los punteros a objetos deben ser del mismo tamaño, de modo 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ático (aunque no sé de una implementación donde lo que sugerí que es verdadero es realmente falso):
§6.2.5 Tipos
¶26 Un puntero a anular 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 tendrán los mismos requisitos de representación y alineación. Todos los punteros a los tipos de estructura deben 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 de funciones, valores de retorno de funciones y miembros de sindicatos.
(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 entre sí y deben compartir los mismos requisitos de alineación, aunque las estructuras a las que apuntan 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 el mismo tamaño y requisitos de alineación. Los punteros a las variaciones de int
(significado unsigned int
y signed int
) deben tener los mismos requisitos de tamaño y alineación entre sí; de manera similar para otros tipos. Pero el estándar C no dice eso formalmente sizeof(int *) == sizeof(void *)
. Oh, bueno, SO 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 tipo 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 de función se puedan convertir en punteros de objeto y viceversa.
Afortunadamente (para los programadores 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 puntero
Todos los tipos de puntero de función tendrán la misma representación que el puntero de tipo a anular. La conversión de un puntero de función a
void *
no alterará la representación. Unvoid *
valor resultante de dicha conversión se puede volver a convertir al tipo de puntero de función original, utilizando una conversión explícita, 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 las conversiones explícitas void *
son muy recomendables para la máxima confiabilidad en el código al pasar un puntero a una función variable como printf()
. En los sistemas POSIX, es seguro emitir 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 void *
no sean un yeso.
dlsym()
función. Algún día escribiré el cambio ... pero 'un día' no es 'hoy'.
void *
ay sin pérdida de información. Pragmáticamente, hay muy pocas máquinas en las que el tamaño de un puntero de función no es el mismo que el de un puntero de objeto. No creo que el estándar proporcione un método para imprimir un puntero de función en máquinas donde la conversión es problemática.
p
es el especificador de conversión para imprimir punteros. Utilizar este.
int a = 42;
printf("%p\n", (void *) &a);
Recuerde que omitir la p
conversión es un comportamiento indefinido y que la impresión con el especificador de conversión 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 se le permita tratar un puntero como cualquier tipo particular de entero, por lo que en realidad 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 diferente o un requisito de alineación diferente unsigned int
?)
*) [¡Vea la excelente respuesta de Jonathan!] Alternativamente %p
, puede usar macros específicas de puntero de <inttypes.h>
, agregadas en C99.
Todos los punteros de objeto son convertibles implícitamente void*
en C, pero para pasar el puntero como un argumento variado, 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);
void *
(aunque printf()
técnicamente necesita la conversión explícita, ya que es una función variada). Los punteros de función no son necesariamente convertibles a void *
.
void *
y vuelvan al puntero de función sin pérdida; afortunadamente, sin embargo, POSIX lo requiere explícitamente (teniendo en cuenta que no es parte del estándar C). Así, en la práctica, puede salirse con la suya (la conversión void (*function)(void)
de void *
ida y vuelta a void (*function)(void)
), pero estrictamente no es un mandato de la norma C.
%u
!
%u
y %lu
están mal en todas las máquinas , no en algunas máquinas. La especificación de printf
es muy clara que cuando el tipo pasado no coincide con el tipo requerido por el especificador de formato, el comportamiento es indefinido. Si el tamaño de los tipos coincide (que podría ser verdadero o falso, dependiendo de la máquina) es irrelevante; son los tipos que deben coincidir, y nunca lo harán.
Como alternativa a las otras respuestas (muy buenas), puede convertir uintptr_t
ao intptr_t
(desde stdint.h
/ inttypes.h
) y usar los correspondientes especificadores de conversión de enteros. 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 typedefs.
#include <stdio.h> 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 %u
especificador de formato? La dirección de la variable en la mayoría de los casos es positiva, ¿puedo usarla en %u
lugar de %p
?
%u
es un formato de unsigned int
tipo y no se puede usar con un argumento puntero a printf
.
Puedes usar %x
o %X
o %p
; Todos ellos son correctos.
%x
, la dirección se da en minúsculas, por ejemplo:a3bfbc4
%X
, la dirección se da en mayúscula, por ejemplo:A3BFBC4
Ambos son correctos.
Si usa %x
o %X
está considerando seis posiciones para la dirección, y si usa %p
está considerando ocho posiciones para la dirección. Por ejemplo:
void*
? De lo contrario, siint*
fueran, digamos, dos bytes yvoid*
fueran 4 bytes, entonces claramente sería un error leer cuatro bytes del argumento, ¿no?