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

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

  •  12-09-2019
  •  | 
  •  

Вопрос

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

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

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?
  • Если да, то существует ли стандартный одобренный портативный способ сделать это?
Это было полезно?

Решение

#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() напечатайте '0x' перед указателем;другие нет (и оба верны в соответствии со стандартом C).

Единственный портативный способ печати значений указателя — использовать "%p" спецификатор, который производит выходные данные, определенные реализацией.(Преобразование в uintptr_t и использование PRIxPTR скорее всего сработает, но (а) uintptr_t был представлен в C99, поэтому некоторые компиляторы могут его не поддерживать, и (б) его существование не гарантируется даже в 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 шестнадцатеричных символов с нулевой передачей на машинах sane*, где они могут быть представлены 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 быть больше минимума, но я предполагаю, что не существует реализации, которая не использовала бы минимум.

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

так что это будет:

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

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

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-битных.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top