Подтвердить что ты не робот

Какое правильное использование 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

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

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

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

Ответ 1

#include <inttypes.h>

#include <stdint.h>

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

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

Ответ 2

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

#include <inttypes.h>

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Этот ответ аналогичен приведенному ранее в fooobar.com/questions/145228/..., но также учитывает возможные ширины (как указано fooobar.com/questions/145228/..., который я не узнал, пока мой ответ не был показан ниже;). Следующие отрезанные будут печатать указатели в виде 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 быть больше, чем минимум, но я предполагаю, что нет реализации, которая не использует минимум.

Ответ 5

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

чтобы оно стало следующим:

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

Ответ 6

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

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

Ответ 7

Возможно, это будет интересно (с 32-битной Windows-машины, используя mingw):

[email protected] /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;
}

[email protected] /u
$ gcc -Wall -o test test.c
test.c: In function `main':
test.c:7: warning: `0' flag used with `%p' printf format

[email protected] /u
$ ./test.exe
p:         0022FF77
x: 000000000022ff77

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

Ответ 8

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

Ответ 9

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

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