Как правильно использовать 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-биттеров.
Мне будет интересно посмотреть, какие ответы есть для портативные решения, так как представление указателя не совсем портативно.