Как правильно использовать printf для отображения указателей, дополненных нулями? - PullRequest
41 голосов
/ 10 августа 2009

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

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

printf("%016p", ptr);

Это работает, но этот gcc выдает следующее сообщение:

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

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

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

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

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

  • Правильно ли мое понимание того, что это поведение не определяется стандартом C99?
  • Если это так, существует ли утвержденный стандартом портативный способ сделать это?

Ответы [ 9 ]

38 голосов
/ 10 августа 2009

#include <inttypes.h>

#include <stdint.h>

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

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

8 голосов
/ 10 августа 2009

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

#include <inttypes.h>

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

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

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

5 голосов
/ 02 августа 2011

Единственный переносимый способ печати значений указателя - это использование спецификатора "%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 шестнадцатеричных цифр на байт.)

4 голосов
/ 01 января 2013

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

0 голосов
/ 05 ноября 2015

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

0 голосов
/ 10 августа 2009

Может быть, это будет интересно (с 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 и приведений (спецификаторы приведений и форматирования крайне непереносимы) используются только нули.

0 голосов
/ 10 августа 2009

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

printf("0x%016lx", (unsigned long)ptr);
0 голосов
/ 10 августа 2009

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

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

printf("%016lx", (unsigned long) ptr);
0 голосов
/ 10 августа 2009

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

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

...