Ответы:
Самый простой ответ, если вы не возражаете против капризов и различий в формате между различными платформами, это стандартная %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 байта, то было бы ошибкой считывать четыре байта из аргумента, не так ли?