Правильный спецификатор формата для печати указателя (адреса)?

Какой спецификатор формата я должен использовать для печати адреса переменной? Я путаюсь между нижележащим лотом.

% u - целое число без знака

% x - шестнадцатеричное значение

% p - указатель void

Каким будет оптимальный формат для печати адреса?

Ответ 1

Самый простой ответ, если вы не возражаете против капризов и вариаций в формате между различными платформами, - это стандартная нотация %p.

Стандарт C99 (ISO/IEC 9899: 1999) гласит в §7.19.6.1 ¶8:

p Аргумент должен быть указателем на void. Значение указателя преобразованный в последовательность печатных символов, в определенном реализацией образом.

(В C11 - ISO/IEC 9899: 2011 - информация приведена в §7.21.6.1 ¶8.)

На некоторых платформах, которые будут включать в себя ведущий 0x, а на других - нет, а буквы могут быть в нижнем или верхнем регистре, а стандарт C даже не определяет, что он должен быть шестнадцатеричный вывод, хотя я знаю, что нет реализации там, где это не так.

Несколько открыта для обсуждения, следует ли явно преобразовывать указатели с приложением (void *). Он явный, что обычно хорошо (так это то, что я делаю), и в стандарте говорится, что "аргумент должен быть указателем на void". На большинстве машин вы бы ушли с отсутствием явного приведения. Тем не менее, это имеет значение на машине, где бит-представление адреса char * для заданной ячейки памяти отличается от адреса "else else pointer" для того же места в памяти. Это был бы адресный адрес, а не байт-адрес, машина. Такие машины не являются обычными (возможно, недоступными) в наши дни, но первая машина, над которой я работал после университета, была одной из таких (ICL Perq).

Если вас не устраивает поведение %p, определяемое реализацией, вместо этого используйте C99 <inttypes.h> и uintptr_t:

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

Это позволяет вам точно настроить представление в соответствии с вашими потребностями. Я решил иметь шестнадцатеричные цифры в верхнем регистре, так что число равномерно одинаковой высоты, и характерный провал в начале 0xA1B2CDEF появляется, таким образом, не как 0xA1B2CDEF, который также падает и вверх по дате. Ваш выбор, хотя, в очень широких пределах. Приведение (uintptr_t) недвусмысленно рекомендуется GCC, когда оно может читать строку формата во время компиляции. Я думаю, что правильно запросить актерский состав, хотя я уверен, что есть люди, которые игнорируют предупреждение и уходят с ним большую часть времени.


Kerrek спрашивает в комментариях:

Я немного запутался в стандартных рекламных акциях и вариативных аргументах. Все ли указатели становятся стандартными для void *? В противном случае, если int* были, скажем, двумя байтами, а void* составляли 4 байта, то, очевидно, было бы ошибкой читать четыре байта из аргумента, не?

Я был под иллюзией, что в стандарте C указано, что все указатели объектов должны быть одного размера, поэтому void * и int * не могут быть разных размеров. Тем не менее, я думаю, что соответствующий раздел стандарта C99 не так актуален (хотя я не знаю о реализации, где то, что я предложил, истинно, на самом деле неверно):

§6.2.5 Типы

¶26 Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. 39) Аналогично, указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковое представление и требования к выравниванию. Все указатели на типы структуры должны иметь одинаковые требования к представлению и согласованию друг с другом. Все указатели на типы объединения должны иметь одинаковые требования к представлению и согласованию друг с другом. Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию.

39) Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращают значения из функций и членов объединений.

(C11 точно так же говорит в разделе §6.2.5, ¶28 и сноске 48.)

Итак, все указатели на структуры должны быть того же размера, что и друг друга, и должны иметь одинаковые требования к выравниванию, хотя структуры, на которые указывают указатели, могут иметь разные требования к выравниванию. Аналогично для профсоюзов. Указатели символов и указатели void должны иметь одинаковые требования к размеру и выравниванию. Указатели на вариации на int (что означает unsigned int и signed int) должны иметь одинаковые требования к размеру и выравниванию друг друга; аналогично для других типов. Но стандарт C официально не утверждает, что sizeof(int *) == sizeof(void *). О, хорошо, это хорошо для того, чтобы вы проверили свои предположения.

Стандарт C окончательно не требует, чтобы указатели на функции были того же размера, что и указатели объектов. Это было необходимо, чтобы не разбить разные модели памяти на DOS-подобных системах. Там вы можете иметь 16-разрядные указатели данных, но 32-битные указатели функций, или наоборот. Вот почему стандарт C не гарантирует, что указатели на функции могут быть преобразованы в указатели объектов и наоборот.

К счастью (для программистов, нацеленных на POSIX), POSIX вступает в нарушение и делает мандат, что указатели на объекты и указатели данных имеют одинаковый размер:

§2.12.3 Типы указателей

Все типы указателей функций должны иметь то же представление, что и указатель типа на void. Преобразование указателя функции в void * не должно изменять представление. Значение void *, полученное в результате такого преобразования, может быть преобразовано обратно в исходный тип указателя функции с использованием явного приведения без потери информации.

Примечание: Стандарт ISO C не требует этого, но он необходим для соответствия POSIX.

Итак, кажется, что явные приведения к void * настоятельно рекомендуются для максимальной надежности кода при передаче указателя на переменную функцию, такую ​​как printf(). В POSIX-системах безопасно отображать указатель на указатель void для печати. В других системах это не обязательно безопасно, и не обязательно безопасно передавать указатели, отличные от void * без трансляции.

Ответ 2

p - спецификатор преобразования для печати указателей. Используйте это.

int a = 42;

printf("%p\n", (void *) &a);

Помните, что опускание приведения - это поведение undefined, а печать с помощью спецификатора преобразования p выполняется в соответствии с реализацией.

Ответ 3

Используйте %p, для "указателя" и не используйте ничего другого *. Вы не гарантированы стандартом, что вам разрешено обрабатывать указатель, как любой тип целого числа, так что вы действительно получите поведение undefined с интегральными форматами. (Например, %u ожидает unsigned int, но что, если void* имеет другое требование размера или выравнивания, чем unsigned int?)

*) [См. подробный ответ Джонатана!] В качестве альтернативы %p вы можете использовать макросы, специфичные для указателя, от <inttypes.h>, добавленные на C99.

Все указатели объектов неявно конвертируются в void* в C, но для того, чтобы передать указатель в качестве вариационного аргумента, вы должны явно его использовать (поскольку указатели на объекты являются только конвертируемыми, но не идентичными указателям void)

printf("x lives at %p.\n", (void*)&x);

Ответ 4

В качестве альтернативы другим (очень хорошим) ответам вы можете использовать uintptr_t или intptr_t (от stdint.h/inttypes.h) и использовать соответствующие спецификаторы преобразования целого числа. Это позволит повысить гибкость в форматировании указателя, но, строго говоря, для предоставления этих typedef не требуется реализация.