2012-01-29 2 views
112

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

% и - целое число без знака

% х - шестнадцатеричное значение

% р - пустой указатель

Какой бы оптимальный формат для печати адреса?

ответ

133

Самый простой ответ, если вы не против капризов и вариацию в формате между различными платформами, является стандартом %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 *, без трансляции.

+3

Я немного смущен стандартными акциями и вариативными аргументами. Все указатели получают стандартное продвижение до 'void *'? В противном случае, если 'int *' были, скажем, двумя байтами, а 'void *' составляли 4 байта, то, очевидно, было бы ошибкой читать четыре байта из аргумента, не? –

+0

Обратите внимание, что обновление для POSIX (POSIX 2013) удалило раздел 2.12.3, переместив большинство требований к ['dlsym()') (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym .html). Однажды я напишу об изменении ... но «один день» не «сегодня». –

+0

Отвечает ли этот ответ на указатели на функции? Могут ли они быть преобразованы в 'void *'? Хм, я вижу ваш комментарий [здесь] (http://stackoverflow.com/questions/9053658/correct-format-specifier-to-print-pointer-address#comment11360489_9053677). Поскольку требуется только преобразование в один ватт (указатель функции на 'void *'), он работает тогда? – chux

28

p - это спецификатор преобразования для печати указателей. Использовать это.

int a = 42; 

printf("%p\n", (void *) &a); 

Напомним, что опуская бросок не определено поведение, и что печать с p спецификатор преобразования делается в определенном реализацией образом.

+0

Прощение, почему отказ от актерского состава - «неопределенное поведение»? Имеет ли этот вопрос адрес какой переменной это, если все, что вам нужно, это адрес, а не значение? – valdo

+8

@valdo, потому что C говорит (C99, 7.19.6.1p8) "p Аргумент должен быть указателем на void." – ouah

+9

@valdo: Не обязательно, чтобы все указатели имели одинаковый размер/представление. – caf

21

Используйте %p, для «указателя» и ничего не используйте *. Вы не гарантированы стандартом, что вам разрешено обрабатывать указатель, как любой конкретный тип целого числа, так что вы фактически получите неопределенное поведение с интегральными форматами. (Например, %u ожидает unsigned int, но что, если void* имеет другой размер или требование выравнивания, чем unsigned int?)

*) [см прекрасный ответ Джонатана!] В качестве альтернативы %p, вы можете использовать указатель конкретных макросов от <inttypes.h>, добавленный в C99.

Все указатели объекта неявно конвертируются в void* в C, но для того, чтобы передать указатель в качестве VARIADIC аргумента, вы должны бросить его в явном виде (так как произвольные указатели объектов являются только кабриолет, но не идентичны к недействительные указатели):

printf("x lives at %p.\n", (void*)&x); 
+2

Все указатели * object * конвертируются в 'void *' (хотя для 'printf()' вам технически нужен явный приведение, так как это вариационная функция). Указатели функций не обязательно конвертируются в 'void *'. – caf

+0

@caf: О, я не знал о вариативных аргументах - исправлено! Благодаря! –

+2

Стандарт C не требует, чтобы указатели функций были конвертированы в 'void *' и обратно указатель на функцию без потерь; К счастью, POSIX явно требует этого (отметив, что он не является частью стандарта C). Таким образом, на практике вы можете с ним справиться (преобразование 'void (* function) (void)' в 'void *' и обратно в 'void (* function) (void)'), но строго это не предусмотрено стандарт C. –

8

В качестве альтернативы других (очень хорошо) ответов, можно отливать в uintptr_t или intptr_t (от stdint.h/inttypes.h) и используйте соответствующие спецификатор целочисленного преобразования. Это обеспечит большую гибкость в форматировании указателя, но, строго говоря, для предоставления этих typedef не требуется реализация.

+0

Ницца, не знал о 'uintptr_t'. – cnicutar

+0

считать '#include int main (void) {int p = 9; int * m = & s; Е ("% U", м); } '__is это неопределенное поведение для печати адреса переменной с использованием спецификатора формата'% u'? __ Адрес переменной в большинстве случаев положителен, поэтому я могу использовать '% u' вместо'% p'? – Destructor

+0

@Destructor: Нет, '% u' является форматом типа' unsigned int' и не может использоваться с аргументом указателя 'printf'. –

 Смежные вопросы

  • Нет связанных вопросов^_^