Qual é o uso apropriado de printf para ponteiros de exibição preenchidos com 0s
Pergunta
Em C, eu gostaria de usar printf para ponteiros de exibição, e de modo que eles se alinham corretamente, eu gostaria de pad-los com 0s.
Meu palpite era que a maneira correta de fazer isso era:
printf("%016p", ptr);
Isso funciona, mas esta gcc reclama com a seguinte mensagem:
warning: '0' flag used with ‘%p’ gnu_printf format
Eu pesquisei um pouco para ele, e da seguinte discussão é sobre o mesmo tema, mas realmente não dá uma solução.
http://gcc.gnu.org/ml/ gcc-bugs / 2003-05 / msg00484.html
Ao lê-lo, parece que a razão pela qual gcc reclama é que a sintaxe sugeri não está definido no C99. Mas eu não consigo encontrar nenhuma outra maneira de fazer a mesma coisa de uma maneira aprovado padrão.
Então, aqui é a dupla pergunta:
- É o meu entendimento correto que este comportamento não é definida pelo padrão C99?
- Se assim for, há um padrão de aprovação, forma portátil de fazer isso?
Solução
#include <inttypes.h>
#include <stdint.h>
printf("%016" PRIxPTR "\n", (uintptr_t)ptr);
mas não vai imprimir o ponteiro da maneira definida de implementação (diz DEAD:BEEF
para 8086 modo segmentado).
Outras dicas
Use:
#include <inttypes.h>
printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer);
Ou use outra variante das macros a partir desse cabeçalho.
Observe também que algumas implementações de printf()
imprimir um '0x
' na frente do ponteiro; outros não (e ambos estão corretos de acordo com o padrão C).
A única portátil maneira de imprimir valores de ponteiro é usar o especificador "%p"
, que produz uma saída definida pela implementação. (Conversão para uintptr_t
e usando PRIxPTR
é susceptível de trabalho, mas (a) uintptr_t
foi introduzido em C99, por isso alguns compiladores não pode apoiá-lo, e (b) não é garantido a existir mesmo em C99 (pode não haver um tipo inteiro grande o suficiente para armazenar todos os valores possíveis de ponteiro.
Assim, o uso "%p"
com sprintf()
(ou snprintf()
, se tiver) para imprimir em uma corda, e, em seguida, imprimir o estofo string com espaços iniciais em branco.
Um problema é que não há nenhuma boa maneira de determinar o comprimento máximo da cadeia produzido por "%p"
.
Esta é reconhecidamente inconveniente (embora é claro que você pode envolvê-lo em uma função). Se você não precisa de portabilidade 100%, eu nunca usei um sistema estavam lançando o ponteiro para unsigned long
e imprimir o resultado usando "0x%16lx"
não iria funcionar. [ Atualizar : Sim, eu tenho. 64-bit Windows tem ponteiros de 64 bits e 32 bits unsigned long
.] (Ou você pode usar "0x%*lx"
e calcular a largura, provavelmente algo como sizeof (void*) * 2
. Se você for realmente paranóico, você pode ser responsável por sistemas onde CHAR_BIT > 8
, para que' vai ter mais de 2 dígitos hexadecimais por byte).
Esta resposta é semelhante à aquela dada anteriormente na https://stackoverflow.com/a/1255185/1905491 mas também leva os possíveis larguras em conta (tal como descrito por https://stackoverflow.com/a/6904396/1905491 quais Eu não reconheci até que a minha resposta foi rendido abaixo dela;). A seguir cortou vai imprimir ponteiros como 8 caracteres hexadecimais 0-passados ??sobre sã * máquinas onde podem ser representados por 32 bits, 16 em 64b e 4 em sistemas 16b.
#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);
Observe o uso do asterisco para buscar a largura do próximo argumento, que está em C99 (provavelmente antes?), Mas que é muito raramente visto "in the wild". Isto é muito melhor do que usar a conversão p
porque este último é definido pela implementação.
* A norma permite uintptr_t
a ser maiores do que o mínimo, mas presumo que não há nenhuma implementação que não usa o mínimo.
É fácil de resolver se você converter o ponteiro para um tipo longo. O problema é esta não irá funcionar em todas as plataformas, uma vez que assume um ponteiro pode caber dentro de um longo, e que um ponteiro pode ser exibido em 16 caracteres (64 bits). Esta é no entanto verdadeiro na maioria das plataformas.
para que ele se tornaria:
printf("%016lx", (unsigned long) ptr);
Tal como o seu link já sugere, o comportamento é indefinido. Eu não acho que há uma maneira portátil de fazer isso como% p si depende da plataforma que você está trabalhando. Você poderia, claro, apenas converter o ponteiro para um int e exibi-lo em hexadecimal:
printf("0x%016lx", (unsigned long)ptr);
Talvez isso será interessante (a partir de uma máquina Windows de 32 bits, usando mingw):
rjeq@RJEQXPD /u
$ cat test.c
#include <stdio.h>
int main()
{
char c;
printf("p: %016p\n", &c);
printf("x: %016llx\n", (unsigned long long) (unsigned long) &c);
return 0;
}
rjeq@RJEQXPD /u
$ gcc -Wall -o test test.c
test.c: In function `main':
test.c:7: warning: `0' flag used with `%p' printf format
rjeq@RJEQXPD /u
$ ./test.exe
p: 0022FF77
x: 000000000022ff77
Como você pode ver, as almofadas% p versão com zeros para o tamanho de um ponteiro e, em seguida espaços para o tamanho especificado, enquanto usando% x e moldes (os moldes e formato especificador são altamente portável) usa apenas zeros.
Note que, se o ponteiro impressões com o prefixo "0x", seria necessário para substituir "% 018p" para compensar.
Eu costumo usar% x para ponteiros de exibição. Suponho que não é portátil para sistemas de 64 bits, mas ele funciona muito bem para 32-bitters.
Eu estarei interessado em ver que respostas existem para soluções portáteis , uma vez que a representação ponteiro não é exatamente portátil.