Qual é o uso apropriado de printf para ponteiros de exibição preenchidos com 0s

StackOverflow https://stackoverflow.com/questions/1255099

  •  12-09-2019
  •  | 
  •  

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?
Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top