A resposta mais simples, supondo que você não se importe com os caprichos e as variações de formato entre plataformas diferentes, é o padrão %p
notação .
A norma C99 (ISO / IEC 9899: 1999) diz em §7.19.6.1.8:
p
O argumento deve ser um ponteiro para void
. O valor do ponteiro é convertido em uma sequência de caracteres de impressão, de maneira definida pela implementação.
(Em C11 - ISO / IEC 9899: 2011 - as informações estão em §7.21.6.1 ¶8.)
Em algumas plataformas, isso incluirá um líder 0x
e, em outros, não, e as letras podem estar em minúsculas ou maiúsculas, e o padrão C nem mesmo define que deve ser uma saída hexadecimal, embora eu saiba nenhuma implementação onde não está.
É um tanto aberto discutir se você deve converter explicitamente os ponteiros com um (void *)
elenco. Isso está sendo explícito, o que geralmente é bom (é o que eu faço), e o padrão diz 'o argumento deve ser um indicador void
'. Na maioria das máquinas, você evitaria omitir uma conversão explícita. No entanto, seria importante em uma máquina onde a representação de bits de um char *
endereço para um determinado local de memória seja diferente do ponteiro ' qualquer outra coisa endereço ' para o mesmo local de memória. Esta seria uma máquina endereçada por palavra, em vez de endereçada por byte. Atualmente, essas máquinas não são comuns (provavelmente não disponíveis), mas a primeira máquina em que trabalhei após a universidade foi uma delas (ICL Perq).
Se você não estiver satisfeito com o comportamento definido pela implementação de %p
, use C99 <inttypes.h>
e, em uintptr_t
vez disso:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Isso permite que você ajuste a representação para se adequar a você. Eu escolhi ter os dígitos hexadecimais em maiúsculas para que o número seja uniformemente da mesma altura e a característica caia no início de 0xA1B2CDEF
aparecer assim, não como o 0xa1b2cdef
que também desce e desce ao longo do número. Sua escolha, porém, dentro de limites muito amplos. O (uintptr_t)
elenco é inequivocamente recomendado pelo GCC quando pode ler a sequência de formato no tempo de compilação. Eu acho que é correto solicitar o elenco, embora eu tenha certeza de que há quem ignore o aviso e se livre dele na maior parte do tempo.
Kerrek pergunta nos comentários:
Estou um pouco confuso sobre promoções padrão e argumentos variados. Todos os ponteiros são promovidos por padrão para anular *? Caso contrário, se houvesse int*
, digamos, dois bytes e void*
quatro bytes, seria claramente um erro ler quatro bytes do argumento, não?
Eu estava sob a ilusão de que o padrão C diz que todos os ponteiros de objeto deve ser do mesmo tamanho, por isso void *
e int *
não podem ter tamanhos diferentes. No entanto, o que eu acho que é a seção relevante do padrão C99 não é tão enfático (embora eu não conheça uma implementação em que o que sugeri que seja verdadeiro seja realmente falso):
§6.2.5 Tipos
§26 Um ponteiro para anular deve ter os mesmos requisitos de representação e alinhamento que um ponteiro para um tipo de caractere. 39) Da mesma forma, os indicadores para versões qualificadas ou não qualificadas de tipos compatíveis devem ter os mesmos requisitos de representação e alinhamento. Todos os ponteiros para tipos de estrutura devem ter os mesmos requisitos de representação e alinhamento que os outros. Todos os ponteiros para tipos de união devem ter os mesmos requisitos de representação e alinhamento que os outros. Ponteiros para outros tipos não precisam ter os mesmos requisitos de representação ou alinhamento.
39) Os mesmos requisitos de representação e alinhamento significam intercambialidade como argumentos para funções, retornam valores de funções e membros de sindicatos.
(C11 diz exatamente o mesmo na seção §6.2.5, §28 e nota de rodapé 48.)
Portanto, todos os ponteiros para estruturas devem ter o mesmo tamanho um do outro e devem compartilhar os mesmos requisitos de alinhamento, mesmo que as estruturas apontadas pelos ponteiros possam ter requisitos de alinhamento diferentes. Da mesma forma para os sindicatos. Ponteiros de caracteres e ponteiros nulos devem ter os mesmos requisitos de tamanho e alinhamento. Os indicadores para variações em int
(significado unsigned int
e signed int
) devem ter os mesmos requisitos de tamanho e alinhamento que os outros; da mesma forma para outros tipos. Mas o padrão C não diz isso formalmente sizeof(int *) == sizeof(void *)
. Bem, SO é bom para fazer você inspecionar suas suposições.
O padrão C definitivamente não requer que os ponteiros de função tenham o mesmo tamanho dos ponteiros de objeto. Isso foi necessário para não quebrar os diferentes modelos de memória em sistemas do tipo DOS. Lá você pode ter ponteiros de dados de 16 bits, mas ponteiros de função de 32 bits, ou vice-versa. É por isso que o padrão C não exige que os ponteiros de função possam ser convertidos em ponteiros de objeto e vice-versa.
Felizmente (para programadores direcionados ao POSIX), o POSIX entra na brecha e exige que ponteiros de função e ponteiros de dados sejam do mesmo tamanho:
§2.12.3 Tipos de ponteiros
Todos os tipos de ponteiros de função devem ter a mesma representação que o ponteiro de tipo a ser anulado. A conversão de um ponteiro de função para void *
não deve alterar a representação. Um void *
valor resultante dessa conversão pode ser convertido novamente no tipo de ponteiro da função original, usando uma conversão explícita, sem perda de informações.
Nota: O padrão ISO C não exige isso, mas é necessário para conformidade com POSIX.
Portanto, parece que os lançamentos explícitos void *
são altamente recomendados para a máxima confiabilidade no código ao passar um ponteiro para uma função variável, como printf()
. Nos sistemas POSIX, é seguro converter um ponteiro de função em um ponteiro nulo para impressão. Em outros sistemas, não é necessariamente seguro fazer isso, nem é necessariamente seguro passar outros indicadores que void *
não sejam vazados.
void*
? Caso contrário, se houvesseint*
, digamos, dois bytes evoid*
quatro bytes, seria claramente um erro ler quatro bytes do argumento, não?dlsym()
função. Um dia vou escrever a mudança ... mas 'um dia' não é 'hoje'.void *
? Hmm, vejo seu comentário aqui . Como é necessária apenas a conversão de um wat (ponteiro de função paravoid *
), ela funciona?void *
retorno e retorno sem perda de informações. Pragmaticamente, existem muito poucas máquinas nas quais o tamanho de um ponteiro de função não é o mesmo que o tamanho de um ponteiro de objeto. Não acho que o padrão forneça um método para imprimir um ponteiro de função em máquinas onde a conversão é problemática.p
é o especificador de conversão para imprimir ponteiros. Usa isto.Lembre-se de que a omissão da
p
conversão é um comportamento indefinido e que a impressão com o especificador de conversão é feita de uma maneira definida pela implementação.fonte
Use
%p
, para "ponteiro", e não use mais nada *. Você não está garantido pelo padrão de que pode tratar um ponteiro como qualquer tipo particular de número inteiro; portanto, obteria um comportamento indefinido com os formatos integrais. (Por exemplo,%u
espera umunsigned int
, mas e sevoid*
tiver um tamanho ou requisito de alinhamento diferenteunsigned int
?)*) [Veja a boa resposta de Jonathan!] Como alternativa
%p
, você pode usar macros específicas de ponteiro de<inttypes.h>
, adicionadas em C99.Todos os ponteiros de objetos são implicitamente conversíveis
void*
em C, mas, para passar o ponteiro como um argumento variável, você deve convertê- lo explicitamente (já que ponteiros de objetos arbitrários são apenas conversíveis , mas não idênticos aos ponteiros nulos):fonte
void *
(emboraprintf()
você precise tecnicamente da conversão explícita, pois é uma função variável). Ponteiros de função não são necessariamente conversíveis emvoid *
.void *
e retornem ao ponteiro de função sem perda; felizmente, porém, o POSIX exige explicitamente isso (observando que não faz parte do padrão C). Assim, na prática, você pode ir longe com ele (a conversãovoid (*function)(void)
paravoid *
e de volta paravoid (*function)(void)
), mas estritamente não está mandatado pelo padrão C.%u
!%u
e%lu
estão errados em todas as máquinas , não em algumas máquinas. A especificação deprintf
é muito clara: quando o tipo passado não corresponde ao tipo exigido pelo especificador de formato, o comportamento é indefinido. Se o tamanho dos tipos corresponde (que pode ser verdadeiro ou falso, dependendo da máquina) é irrelevante; são os tipos que devem corresponder e nunca serão.Como uma alternativa para os outros (muito bom) respostas, você pode converter para
uintptr_t
ouintptr_t
(destdint.h
/inttypes.h
) e use os especificadores de conversão inteiro correspondente. Isso permitiria mais flexibilidade na formatação do ponteiro, mas estritamente falando, uma implementação não é necessária para fornecer esses typedefs.fonte
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
é um comportamento indefinido para imprimir o endereço da variável usando o%u
especificador de formato? O endereço da variável na maioria dos casos é positivo, então posso usar em%u
vez de%p
?%u
é um formato para ounsigned int
tipo e não pode ser usado com um argumento de ponteiro paraprintf
.Você pode usar
%x
ou%X
ou%p
; todos eles estão corretos.%x
, o endereço será fornecido em minúsculas, por exemplo:a3bfbc4
%X
, o endereço será fornecido em maiúsculas, por exemplo:A3BFBC4
Ambos estão corretos.
Se você usar
%x
ou%X
considerar seis posições para o endereço, e se você usar%p
, considerar oito posições para o endereço. Por exemplo:fonte