Самый простой ответ, если вы не против капризов и вариацию в формате между различными платформами, является стандартом %p
обозначения.
Стандарт C99 (ISO/IEC 9899: 1999) говорит, что в §7.19.6.1 ¶8:
p
Аргумент должен быть указателем на void
. Значение указателя равно , преобразованное в последовательность символов печати, в определенном реализацией порядке .
(В С11 - ISO/IEC 9899: 2011 - информация в §7.21.6.1 ¶8.)
На некоторых платформах, которые будут включать в себя ведущую 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, когда он может прочитать строку формата во время компиляции. Я думаю, что правильно запросить приведение, хотя я уверен, что есть люди, которые будут игнорировать предупреждение и уходить с ним большую часть времени.
Kerrek спрашивает в комментариях:
Я немного запутался о стандартных рекламных и переменным числом аргументов. Все ли указатели становятся стандартными для void *? В противном случае, если int*
были, скажем, двумя байтами, и void*
составляли 4 байта, то, очевидно, было бы ошибкой читать четыре байта из аргумента, не?
Я был под иллюзией, что стандарт C говорит, что все указатели объектов должны быть одинакового размера, так void *
и int *
не могут быть разных размеров. Однако, как мне кажется, соответствующий раздел стандарта C99 не так решительно (хотя я не знаю реализации, где то, что я предложил это верно на самом деле ложь):
§6.2.5 Типы
¶26 Указатель на пустоту должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. 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 Pointer Types
Все типы указателей функции должны иметь то же представление, что и указатель типа на void. Преобразование указателя функции в void *
не должно изменять представление. Значение void *
, полученное в результате такого преобразования, может быть преобразовано обратно в исходный тип указателя функции с использованием явного приведения без потери информации.
Примечание: Стандарт ISO C не требует этого, но он необходим для соответствия POSIX.
Таким образом, похоже, что явное приведение к void *
сильно рекомендуется для обеспечения максимальной надежности в коде при передаче указателя на VARIADIC функции, такие как printf()
. В POSIX-системах безопасно отображать указатель на указатель void для печати. В других системах это не обязательно безопасно, и не обязательно безопасно передавать указатели, отличные от void *
, без трансляции.
Я немного смущен стандартными акциями и вариативными аргументами. Все указатели получают стандартное продвижение до 'void *'? В противном случае, если 'int *' были, скажем, двумя байтами, а 'void *' составляли 4 байта, то, очевидно, было бы ошибкой читать четыре байта из аргумента, не? –
Обратите внимание, что обновление для POSIX (POSIX 2013) удалило раздел 2.12.3, переместив большинство требований к ['dlsym()') (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym .html). Однажды я напишу об изменении ... но «один день» не «сегодня». –
Отвечает ли этот ответ на указатели на функции? Могут ли они быть преобразованы в 'void *'? Хм, я вижу ваш комментарий [здесь] (http://stackoverflow.com/questions/9053658/correct-format-specifier-to-print-pointer-address#comment11360489_9053677). Поскольку требуется только преобразование в один ватт (указатель функции на 'void *'), он работает тогда? – chux