Правильный ли спецификатор формата для печати указателя или адреса?
Какой спецификатор формата мне следует использовать для вывода адреса переменной? Я запутался между нижеуказанным лотом.
% u - целое число без знака
%x - шестнадцатеричное значение
%p - пустой указатель
Какой формат будет оптимальным для печати адреса?
6 ответов
Самый простой ответ, если вы не возражаете против капризов и различий в формате между различными платформами, это стандарт %p
нотации.
Стандарт C99 (ISO/IEC 9899:1999) в §7.19.6.1.68 гласит:
p
Аргумент должен быть указателем наvoid
, Значение указателя преобразуется в последовательность печатных символов способом, определяемым реализацией.
(В C11 - ISO/IEC 9899:2011 - информация в §7.21.6.1.18.)
На некоторых платформах это будет включать 0x
а для других - нет, и буквы могут быть в нижнем или верхнем регистре, и стандарт C даже не определяет, что это должен быть шестнадцатеричный вывод, хотя я не знаю ни одной реализации, где это не так.
В какой-то степени открыта дискуссия о том, следует ли явно преобразовывать указатели с помощью (void *)
бросать. Это явно, что обычно хорошо (так я и делаю), и стандарт говорит: "аргумент должен быть указателем на void
". На большинстве машин вам не нужно указывать явное приведение. Тем не менее, это будет иметь значение на машине, где битовое представление char *
адрес для данной ячейки памяти отличается от адреса "чего-либо еще" для той же ячейки памяти. Это будет машинный адрес с адресом слова, а не с байтовым адресом. Такие машины не распространены (вероятно, недоступны) в наши дни, но первая машина, над которой я работал после университета, была одной из таких (ICL Perq).
Если вас не устраивает поведение, определяемое реализацией %p
затем используйте C99 <inttypes.h>
а также uintptr_t
вместо:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Это позволяет вам точно настроить представление под себя. Я выбрал шестнадцатеричные цифры в верхнем регистре, чтобы число было одинаково одинаковым по высоте и характерному провалу в начале 0xA1B2CDEF
появляется так, а не как 0xa1b2cdef
который наклоняется вверх и вниз по номеру тоже. Ваш выбор, хотя, в очень широких пределах. (uintptr_t)
GCC однозначно рекомендует приведение, когда оно может прочитать строку формата во время компиляции. Я думаю, что правильно запрашивать актерский состав, хотя я уверен, что есть некоторые, которые игнорируют предупреждение и избегают его большую часть времени.
Керрек спрашивает в комментариях:
Я немного запутался в стандартных акциях и разнообразных аргументах. Все ли указатели стандартно повышаются до void*? В противном случае, если
int*
были, скажем, два байта, иvoid*
было 4 байта, тогда было бы ошибкой считать четыре байта из аргумента, не так ли?
У меня была иллюзия, что стандарт C говорит, что все указатели объектов должны быть одинакового размера, поэтому void *
а также int *
не может быть разных размеров. Однако то, что я считаю соответствующим разделом стандарта C99, не столь решительно (хотя я не знаю реализации, где то, что я предложил, является истинным, на самом деле является ложным):
§6.2.5 Типы
A26 Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа.39) Аналогично, указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковые требования к представлению и выравниванию. Все указатели на типы конструкций должны иметь те же требования к представлению и выравниванию, что и другие. Все указатели на типы объединений должны иметь те же требования к представлению и выравниванию, что и другие. Указатели на другие типы не обязательно должны иметь одинаковые требования к представлению или выравниванию.
39) Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращаемых значений из функций и членов объединений.
(C11 говорит то же самое в §6.2.5, ¶28 и сноске 48.)
Таким образом, все указатели на структуры должны быть одинакового размера и должны иметь одинаковые требования выравнивания, даже если структуры, на которые указывают указатели, могут иметь разные требования выравнивания. Аналогично для профсоюзов. Указатели на символы и указатели на пустоту должны иметь одинаковые требования к размеру и выравниванию. Указатели на вариации на 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 *
без актерского состава.
p
является спецификатором преобразования для печати указателей. Использовать этот.
int a = 42;
printf("%p\n", (void *) &a);
Помните, что пропуск броска - неопределенное поведение, и что печать с p
спецификатор преобразования выполняется способом, определяемым реализацией.
Использование %p
, для "указателя", и не используйте ничего другого *. Стандарт не гарантирует, что вы можете обрабатывать указатель как любой конкретный тип целого числа, поэтому на самом деле вы получите неопределенное поведение с интегральными форматами. (Например, %u
ожидает unsigned int
, но что, если void*
имеет другой размер или требование выравнивания, чем unsigned int
?)
*) [См. Хороший ответ Джонатана!] В качестве альтернативы %p
, вы можете использовать специфичные для указателя макросы из <inttypes.h>
, добавлено в C99.
Все указатели объектов неявно преобразуются в void*
в C, но для того, чтобы передать указатель как переменный аргумент, вы должны привести его явно (так как произвольные указатели объектов могут быть преобразованы только, но не идентичны указателям void):
printf("x lives at %p.\n", (void*)&x);
В качестве альтернативы другим (очень хорошим) ответам, вы можете привести uintptr_t
или же intptr_t
(от stdint.h
/inttypes.h
) и использовать соответствующие целочисленные спецификаторы преобразования. Это обеспечит большую гибкость при форматировании указателя, но, строго говоря, реализация не обязана предоставлять эти typedefs.
Ты можешь использовать %x
или %X
или %p
; все они верны.
- Если вы используете
%x
, адрес указывается в нижнем регистре, например:a3bfbc4
- Если вы используете
%X
, адрес указывается в верхнем регистре, например:A3BFBC4
Оба они верны.
Если вы используете %x
или %X
он рассматривает шесть позиций для адреса, и если вы используете %p
для адреса рассматривается восемь позиций. Например:
Стандарт%p
.
Если%x
печатайте только части всего адреса в 64-битной системе, используйте%lx
или%llx
вместо.