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

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

% 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вместо.

Другие вопросы по тегам