Как правильно использовать printf для отображения указателей, дополненных 0s

В C я хотел бы использовать printf для отображения указателей, и чтобы они выстраивались правильно, я хотел бы дополнить их 0s.

Я думаю, что правильный способ сделать это:

printf("%016p", ptr);

это работает, но этот gcc жалуется со следующим сообщением:

warning: '0' flag used with ‘%p’ gnu_printf format

Я немного погуглил для него, и следующий поток находится на той же теме, но на самом деле не дает решение.

http://gcc.gnu.org/ml/gcc-bugs/2003-05/msg00484.html

читая, кажется, что причину ССЗ жалуется, что синтаксис я предложил не определено в стандарте C99. Но я не могу найти другого способа сделать то же самое стандартным одобренным способом.

Итак, вот двойной вопрос:

  • правильно ли я понимаю, что это поведение не определяется стандартом C99?
  • если это так, есть ли одобренный стандарт, портативный способ сделать это?

9 ответов


#include <inttypes.h>

#include <stdint.h>

printf("%016" PRIxPTR "\n", (uintptr_t)ptr);

но он не будет печатать указатель определенным образом (говорит DEAD:BEEF для 8086 сегментированного режима).


использование:

#include <inttypes.h>

printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer);

или используйте другой вариант макросов из этого заголовка.

также обратите внимание, что некоторые реализации printf() печать a'0x ' перед указателем; другие не делают (и оба правильны согласно стандарту C).


только портативный способ печати значений указателей-использовать "%p" спецификатор, который производит выход, определенный реализацией. (Преобразование в uintptr_t и с помощью PRIxPTR вероятно, будет работать, но (a)uintptr_t был введен в C99, поэтому некоторые компиляторы могут не поддерживать его, и (b) он не гарантируется даже в C99 (может не быть целочисленного типа, достаточно большого, чтобы содержать все возможные значения указателя.

чтобы использовать "%p" с sprintf() (или snprintf(), если вы имейте это), чтобы напечатать строку, а затем распечатать строку заполнения с ведущими пробелами.

одна проблема заключается в том, что нет действительно хорошего способа определить максимальную длину строки, производимой "%p".

следует признать, что это неудобно (хотя, конечно, вы можете обернуть его в функцию). Если вам не нужна 100% переносимость, я никогда не использовал систему, бросая указатель на unsigned long и печать результата с помощью "0x%16lx" не будет работать. [обновление: Да, есть. 64-разрядная Windows имеет 64-разрядные указатели и 32-разрядные unsigned long.] (Или вы можете использовать "0x%*lx" и вычислить ширину, возможно, что-то вроде sizeof (void*) * 2. Если вы действительно параноик, вы можете учитывать системы, где CHAR_BIT > 8, таким образом, вы получите более 2 шестнадцатеричных цифр на байт.)


этот ответ похож на тот, который был дан ранее в https://stackoverflow.com/a/1255185/1905491 но также учитывает возможные ширины (как указано https://stackoverflow.com/a/6904396/1905491 который я не узнал, пока мой ответ не был приведен ниже;). Следующие обрезанные будут печатать указатели в виде 8 0-переданных шестнадцатеричных символов на нормальных* машинах, где они могут быть представлены 32 битами, 16 на 64b и 4 на 16b системный.

#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))

printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);

обратите внимание на использование символа звездочки для получения ширины следующим аргументом, который находится в C99 (возможно, раньше?), но который довольно редко встречается "в природе". Это лучше, чем использовать p преобразование, потому что последнее определено реализацией.

* стандарт позволяет uintptr_t быть больше минимума, но я предполагаю, что нет реализации, которая не использует минимум.


Это легко решить, если вы разыгрываете указатель на тип long. Проблема в том, что это не будет работать на всех платформах, поскольку предполагается, что указатель может поместиться внутри long, и что указатель может отображаться в 16 символах (64 бит). Однако это верно для большинства платформ.

так это стало бы:

printf("%016lx", (unsigned long) ptr);

Как уже предполагает ваша ссылка, поведение не определено. Я не думаю, что есть портативный способ сделать это, поскольку %p зависит от платформы, над которой вы работаете. Вы можете, конечно, просто бросить указатель на int и отобразить его в hex:

printf("0x%016lx", (unsigned long)ptr);

возможно, это будет интересно (с 32-разрядной машины windows, используя 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

Как вы можете видеть, %P версии колодки с нулями до размера указателя, а затем пробелы до указанного размера, в то время как использование %x и приведений (приведения и спецификатор формата очень непортящиеся) использует только нули.


обратите внимание, что если указатель печатает с префиксом "0x", вам нужно будет заменить "%018p" для компенсации.


обычно я использую %x для отображения указателей. Я полагаю, что это не портативно для 64-битных систем, но отлично работает для 32-биттеров.

Мне будет интересно посмотреть, какие ответы есть для портативные решения, так как представление указателя не совсем портативно.