Ответы:
Самый простой ответ, если вы не возражаете против капризов и различий в формате между различными платформами, это стандартная %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 *
адреса для данной ячейки памяти отличается от адреса « указателя чего-либо еще » для той же ячейки памяти. Это будет машинный адрес с адресом слова, а не с байтовым адресом. Такие машины не распространены (вероятно, недоступны) в наши дни, но первая машина, над которой я работал после университета, была одной из таких (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 *
без приведения.
dlsym()
вместо этого большинство функций перенесено в функцию. Однажды я запишу изменения ... но «один день» не «сегодня».
void *
? Хм, я вижу твой комментарий здесь . Поскольку требуется преобразование только в один ватт (указатель на функцию void *
), тогда оно работает?
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);
void *
(хотя для printf()
вас технически требуется явное приведение, так как это функция с переменным числом аргументов). Указатели на функции не обязательно конвертируемы в void *
.
void *
и обратно без потерь; к счастью, POSIX явно требует этого (отмечая, что он не является частью стандарта C). Таким образом, на практике вы можете сойти с рук (преобразование void (*function)(void)
в void *
и обратно void (*function)(void)
), но строго это не предусмотрено стандартом C.
%u
!
%u
и %lu
ошибаются на всех машинах , а не на некоторых машинах. Спецификация printf
очень ясна: когда переданный тип не соответствует типу, требуемому спецификатором формата, поведение не определено. Соответствует ли размер типов (который может быть истинным или ложным, в зависимости от машины) не имеет значения; это типы, которые должны соответствовать, и они никогда не будут соответствовать.
В качестве альтернативы другим (очень хорошим) ответам вы можете привести к uintptr_t
или intptr_t
(из stdint.h
/ inttypes.h
) и использовать соответствующие целочисленные спецификаторы преобразования. Это обеспечит большую гибкость форматирования указателя, но, строго говоря, реализация не обязана предоставлять эти typedefs.
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
это неопределенное поведение для печати адреса переменной с использованием %u
спецификатора формата? Адрес переменной в большинстве случаев положительный, так что я могу использовать %u
вместо %p
?
%u
это формат для unsigned int
типа и не может использоваться с аргументом указателя на printf
.
Вы можете использовать %x
или %X
или %p
; все они верны.
%x
, адрес дается в нижнем регистре, например:a3bfbc4
%X
, адрес дается в верхнем регистре, например:A3BFBC4
Оба они верны.
Если вы используете %x
или %X
он рассматривает шесть позиций для адреса, и если вы используете, %p
он рассматривает восемь позиций для адреса. Например:
void*
? В противном случае, если быint*
, скажем, два байта иvoid*
были 4 байта, то было бы ошибкой считывать четыре байта из аргумента, не так ли?