Відповіді:
Найпростіша відповідь, якщо припустити, що ви не заперечуєте проти капризів та змін у форматі між різними платформами, - це стандартне %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)
За нке литим однозначно рекомендуються , коли він може читати формат рядок під час компіляції. Я вважаю, що правильно запитувати виступ, хоча я впевнений, що є такі, хто б ігнорував попередження і пішов з ним більшу частину часу.
Керрек питає в коментарях:
Я трохи розгублений щодо стандартних акцій та різноманітних аргументів. Чи всі покажчики стандартно підвищуються до недійсних *? Інакше, якби
int*
, скажімо, були два байти іvoid*
були 4 байти, то явно було б помилкою прочитати чотири байти з аргументу, чи не так?
Я був під ілюзією , що стандарт C каже , що всі покажчики об'єктів повинні бути однакового розміру, тому void *
і int *
не може бути різних розмірів. Однак те, що, на мою думку, є відповідним розділом стандарту C99, не настільки наполегливе (хоча я не знаю про реалізацію, де те, що я запропонував, є дійсно хибним):
§6.2.5 Види
¶26 Покажчик недійсного має відповідати вимогам представлення та вирівнювання, як і вказівник на тип символів. 39) Аналогічно, покажчики на кваліфіковані або некваліфіковані версії сумісних типів повинні мати однакові вимоги щодо представлення та вирівнювання. Усі вказівники на типи структур мають однакові вимоги щодо представлення та вирівнювання, як один до одного. Усі вказівники на типи об'єднання мають однакові вимоги щодо представлення та вирівнювання, як один до одного. Покажчики інших типів не повинні мати однакових вимог щодо представлення чи вирівнювання.
39) Ті ж вимоги щодо представлення та вирівнювання мають на увазі взаємозамінність як аргументи до функцій, повернення значень функцій та членів союзів.
(C11 говорить точно так само в розділі §6.2.5, ¶28 та виносці 48.)
Отже, всі вказівники на структури повинні бути однакового розміру, і вони повинні мати однакові вимоги до вирівнювання, хоча структури, на які вказують вказівники, можуть мати різні вимоги до вирівнювання. Аналогічно і для спілок. Покажчики символів та недійсні покажчики повинні мати однакові вимоги щодо розміру та вирівнювання. Покажчики на варіації на int
(значення unsigned int
та signed int
) повинні мати однакові вимоги до розміру та вирівнювання, як один до одного; аналогічно для інших типів. Але стандарт С офіційно цього не говорить sizeof(int *) == sizeof(void *)
. Ну добре, так добре, щоб змусити вас перевірити свої припущення.
Останнє стандарт C не вимагає, щоб покажчики функцій були такого ж розміру, як вказівники об'єкта. Це було необхідно, щоб не порушувати різні моделі пам’яті на DOS-подібних системах. Там ви можете мати 16-бітні покажчики даних, але 32-бітні функціональні вказівники, або навпаки. Ось чому стандарт C не передбачає, що функціональні вказівники можуть бути перетворені в об'єктні покажчики і навпаки.
На щастя (для програмістів, націлених на POSIX), POSIX вступає в порушення та виконує мандат, що покажчики та покажчики даних мають однаковий розмір:
§2.12.3 Типи вказівників
Усі типи вказівників функцій повинні мати те саме представлення, що і покажчик типу на недійсні. Перетворення покажчика функції в
void *
не повинно змінювати подання.void *
Значення в результаті такого перетворення може бути перетворена назад у вихідний тип покажчика функції, використовуючи явне приведення, без втрати інформації.Примітка: Стандарт ISO C цього не вимагає, але він необхідний для відповідності POSIX.
Отже, здається, що явні касти void *
настійно доцільні для досягнення максимальної надійності коду при передачі покажчика на різноманітну функцію, таку як printf()
. У системах POSIX можна безпечно вводити покажчик функції на недійсний покажчик для друку. В інших системах це не обов'язково безпечно, а також не обов'язково безпечно передавати вказівники, окрім як void *
без черги.
dlsym()
функції. Одного разу я напишу зміну ... але "один день" - це не "сьогодні".
void *
і назад без втрати інформації. Прагматично, дуже мало машин, де розмір вказівника функції не такий, як розмір вказівника об'єкта. Я не думаю, що стандарт пропонує метод друку вказівника функції на машинах, де перетворення проблематично.
p
є специфікатором перетворення для покажчиків друку. Використовуй це.
int a = 42;
printf("%p\n", (void *) &a);
Пам’ятайте, що пропускати трансляцію - це невизначена поведінка і що друк із p
специфікатором перетворення виконується певним чином.
Використовуйте %p
для "вказівника" і не використовуйте нічого іншого *. Ви не гарантуєте стандартом, що вам дозволено трактувати покажчик, як будь-який конкретний тип цілого числа, так що ви фактично отримаєте не визначене поведінку з цілісними форматами. (Наприклад, %u
очікує unsigned int
, але що робити, якщо void*
вимоги щодо розміру чи вирівнювання мають інший розмір, ніж unsigned int
?)
*) [Див. Точну відповідь Джонатана!] Альтернативно %p
, ви можете використовувати макроси, орієнтовані на вказівники <inttypes.h>
, додані в C99.
Усі вказівники об'єкта неявно перетворюються void*
в C, але для того, щоб передати вказівник як різноманітний аргумент, його потрібно чітко надати (оскільки довільні об’єкти покажчики є лише конвертованими , але не ідентичними покажчикам недійсних):
printf("x lives at %p.\n", (void*)&x);
void *
(хоча для printf()
вас технічно потрібен чіткий склад, оскільки це варіативна функція). Покажчики функцій не обов'язково перетворюються на void *
.
void *
на функціональний вказівник та поверталися до нього без втрат; на щастя, однак POSIX цього прямо вимагає (зазначивши, що він не є частиною стандарту C). Так, на практиці ви можете піти від цього (перетворюючись void (*function)(void)
на void *
та назад void (*function)(void)
), але суворо це не передбачено стандартом С.
%u
!
%u
і %lu
помиляються на всіх машинах , а не на деяких машинах. У специфікації printf
дуже зрозуміло, що коли тип, що передається, не відповідає типу, який вимагає специфікатор формату, поведінка не визначена. Не має значення розмір відповідності типів (який може бути правдивим чи хибним, залежно від машини); це типи, які повинні відповідати, і вони ніколи не будуть.
В якості альтернативи іншим (дуже хорошим) відповідям, ви можете подати на ( uintptr_t
або intptr_t
від stdint.h
/ inttypes.h
) і використовувати відповідні цілі специфікатори перетворення. Це дозволило б забезпечити більшу гнучкість у форматуванні покажчика, але строго кажучи, для надання цих типівdede не потрібно виконання.
#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 байти, то явно було б помилкою прочитати чотири байти з аргументу, чи не так?