Corrigir o especificador de formato para imprimir o ponteiro (endereço)?

Qual especificador de formato devo usar para imprimir o endereço de uma variável? Estou confuso entre o lote abaixo.

% u – inteiro sem sinal

% x – valor hexadecimal

% p – ponteiro nulo

Qual seria o melhor formato para imprimir um endereço?

A resposta mais simples, supondo que você não se importa com os caprichos e variações no formato entre diferentes plataformas, é a notação %p padrão.

O padrão C99 (ISO / IEC 9899: 1999) diz no §7.19.6.1 ¶8:

p O argumento deve ser um ponteiro para void . O valor do ponteiro é convertido em uma seqüência de caracteres de impressão, de uma maneira definida pela implementação.

(Em C11 – ISO / IEC 9899: 2011 – a informação está em §7.21.6.1 ¶8.)

Em algumas plataformas, isso includeá um 0x principal 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 saída hexadecimal, embora eu saiba de nenhuma implementação onde não é.

É um tanto aberto debater se você deve converter explicitamente os pointers com um casting (void *) . Está sendo explícito, o que geralmente é bom (assim é o que eu faço), e o padrão diz “o argumento deve ser um ponteiro para o void “. Na maioria das máquinas, você se safaria omitindo um casting explícito. No entanto, isso seria importante em uma máquina em que a representação de bit de um endereço char * para um determinado local de memory seja diferente do endereço do ‘ qualquer outro ponteiro ‘ para o mesmo local de memory. Essa seria uma máquina endereçada por palavra, em vez de endereçada por byte. Tais máquinas não são comuns (provavelmente não disponíveis) atualmente, mas a primeira máquina em que trabalhei depois da universidade foi uma dessas (ICL Perq).

Se você não estiver satisfeito com o comportamento definido pela implementação de %p , use C99 e uintptr_t :

 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 a mesma altura e a 0xA1B2CDEF característica no início de 0xA1B2CDEF apareça assim, não como 0xa1b2cdef que também sobe e desce ao longo do número. Sua escolha, porém, dentro de limites muito amplos. O cast (uintptr_t) é recomendado de forma não ambígua pelo GCC quando ele pode ler a string de formato no momento da compilation. Eu acho que é correto solicitar o casting, embora eu tenha certeza que há alguns que ignoram o aviso e fogem com ele a maior parte do tempo.


Kerrek pergunta nos comentários:

Estou um pouco confuso sobre promoções padrão e argumentos variadicos. Todos os pointers são promovidos por padrão para anular *? Caso contrário, se int* fosse, digamos, dois bytes e void* fossem 4 bytes, então seria claramente um erro ler quatro bytes do argumento, non?

Eu estava sob a ilusão de que o padrão C diz que todos os pointers de object devem ter o mesmo tamanho, então 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 saiba de uma implementação onde o que eu sugeri é verdade é falso):

§6.2.5 Tipos

¶26 Um ponteiro para void deve ter os mesmos requisitos de representação e alinhamento que um ponteiro para um tipo de caractere. 39) Da mesma forma, pointers para versões qualificadas ou não qualificadas de tipos compatíveis devem ter os mesmos requisitos de representação e alinhamento. Todos os pointers para os tipos de estrutura devem ter os mesmos requisitos de representação e alinhamento que os outros. Todos os pointers para tipos de união devem ter os mesmos requisitos de representação e alinhamento um do outro. Ponteiros para outros tipos não precisam ter a mesma representação ou requisitos de alinhamento.

39) Os mesmos requisitos de representação e alinhamento devem implicar a permutabilidade como argumentos para funções, valores de retorno 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 pointers para estruturas devem ter o mesmo tamanho que os outros e devem compartilhar os mesmos requisitos de alinhamento, mesmo que as estruturas apontadas pelos pointers possam ter requisitos de alinhamento diferentes. Da mesma forma para os sindicatos. Os pointers de caractere e os pointers nulos devem ter os mesmos requisitos de tamanho e alinhamento. Ponteiros para variações em int (significando unsigned int signed int e unsigned int signed int ) devem ter o mesmo tamanho e requisitos de alinhamento um do outro; similarmente para outros tipos. Mas o padrão C não diz formalmente que sizeof(int *) == sizeof(void *) . Oh bem, isso é bom para fazer você inspecionar suas suposições.

O padrão C definitivamente não exige que os pointers de function tenham o mesmo tamanho que os pointers de object. Isso foi necessário para não quebrar os diferentes modelos de memory em sistemas do tipo DOS. Lá você poderia ter pointers de dados de 16 bits, mas pointers de function de 32 bits ou vice-versa. É por isso que o padrão C não exige que os pointers de function possam ser convertidos em pointers de object e vice-versa.

Felizmente (para programadores que visam o POSIX), o POSIX entra na brecha e determina que os pointers de function e os pointers de dados têm o mesmo tamanho:

§2.12.3 Tipos de Ponteiro

Todos os tipos de ponteiro de function devem ter a mesma representação que o ponteiro de tipo para anular. A conversão de um ponteiro de function para void * não deve alterar a representação. Um valor void * resultante de tal conversão pode ser convertido de volta para o tipo de ponteiro de function original, usando uma conversão explícita, sem perda de informação.

Nota: O padrão ISO C não exige isso, mas é necessário para conformidade com POSIX.

Portanto, parece que conversões explícitas para void * são fortemente aconselhadas para confiabilidade máxima no código ao passar um ponteiro para uma function variádica, como printf() . Em sistemas POSIX, é seguro converter um ponteiro de function em um ponteiro vazio para impressão. Em outros sistemas, não é necessariamente seguro fazê-lo, nem é necessariamente seguro passar pointers que não void * sem casting.

p é o especificador de conversão para imprimir pointers. Usa isto.

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

Lembre-se de que omitir o casting é um comportamento indefinido e que a impressão com o especificador de conversão p é feita de maneira definida pela implementação.

Use %p , para “ponteiro”, e não use mais nada *. Você não está garantido pelo padrão que tem permissão para tratar um ponteiro como qualquer tipo particular de inteiro, então você realmente obteria um comportamento indefinido com os formatos integrais. (Por exemplo, %u espera um unsigned int , mas e se void* tiver um tamanho diferente ou um requisito de alinhamento diferente de unsigned int ?)

*) [Veja a resposta bem de Jonathan!] Como alternativa a %p , você pode usar macros específicas de ponteiro de , adicionadas em C99.

Todos os pointers de object são implicitamente conversíveis para void* em C, mas para passar o ponteiro como um argumento variadico, é necessário convertê- lo explicitamente (já que pointers de objects arbitrários são apenas conversíveis , mas não idênticos a pointers de void):

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

Como uma alternativa para as outras respostas (muito boas), você pode converter para uintptr_t ou intptr_t (de stdint.h / inttypes.h ) e usar os especificadores de conversão de números inteiros correspondentes. Isso permitiria mais flexibilidade na forma como o ponteiro é formatado, mas, estritamente falando, não é necessário que uma implementação forneça esses typedefs.