printf ("%p") и литье в (void *)

в недавнем вопросе кто-то упомянул, что при печати значения указателя с помощью printf вызывающий должен привести указатель к void *, например:

int *my_ptr = ....

printf("My pointer is: %p", (void *)my_ptr);

хоть убей, я не могу понять, почему. Я нашел этот вопрос, что почти то же самое. Ответ на вопрос правильный-он объясняет, что ints и указатели не обязательно одинаковой длины.

это, конечно, правда, но когда я уже есть указатель, как в приведенном выше случае, почему я должен бросать из int * to void *? Когда int * отличается от void *? На самом деле, когда делает (void *)my_ptr генерировать машинный код, который отличается от просто my_ptr?

обновление: Несколько знающих респондентов процитировали стандарт, сказав, что передача неправильного типа может привести к неопределенному поведению. Как? Я ожидаю printf("%p", (int *)ptr) и printf("%p", (void *)ptr) для генерации точно такого же стека-кадра. Когда два вызова будут генерировать разные кадры стека?

5 ответов


в языке C все типы указателей потенциально различаются по своим представлениям. Так что да, int * отличается от void *. Реальную платформу, которая иллюстрировала бы это различие, может быть трудно (или невозможно) найти, но на концептуальном уровне различие все еще существует.

иными словами, В общем случае разные типы указателей разные представления. int * отличается от void * отличается от double *. Дело в том, что ваша платформа использует то же представление для void * и int * Это не что иное, как совпадение, что касается языка C.

язык утверждает, что некоторые типы указателей должны иметь идентичные представления, которые включают void * и char *, указатели на различные типы структур или, скажем,int * и const int *. Но это лишь исключения из общего правила.


p спецификатор преобразования требует аргумент типа void *. Если вы не передадите аргумент типа void *, вызов функции вызывает неопределенное поведение.

из стандарта C:

(C11, 7.21.6.1p8 форматированные функции ввода / вывода) "P аргумент должен быть указателем на void."

типы указателей в C не должны иметь одинаковый размер или одинаковое представление.

пример реализации с различными типами указателей представление Cray PVP, где представление типов указателей 64-бит для void * и char * но 32-bit для других типов указателей.

См. " Справочное Руководство Cray C/C++", Таблица 3. в "9.1.2.2" http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf


другие люди адекватно рассмотрели случай прохождения int * до прототип функции с фиксированным числом аргументов что ожидает другой тип указателя.

printf нет такой функции. Это вариационная функция, поэтому по умолчанию аргумент акции используются для его анонимных аргументов (т. е. все после строки формата), и если продвигаемый тип каждого аргумента не ровно соответствовать типу ожидаемое эффектором формата поведение не определено. В частности, даже если int * и void * имеют одинаковое представление,

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

имеет неопределенное поведение.

это так макет кадра вызова может зависеть от точного конкретного типа каждого аргумента. ABIs, которые указывают различные области аргументов для типов указателей и не указателей, произошли в реальной жизни (например, Motorola 68000 хотела бы, чтобы вы сохранили указатели в регистрах адресов и не указатели в регистрах данных в максимально возможной степени). Я не знаю ни одного реального ABI, который отделяется от distinct указатель типы, но это разрешено, и я не удивлюсь, если услышу об этом.


на самом деле, за исключением древних мэйнфреймов/мини, разные типы указателей крайне маловероятны, чтобы иметь разные размеры. Однако у них разные типы, и в спецификацию для printf, вызывая его с неправильным аргументом типа для спецификатора формата, приводит к неопределенное поведение. Это значит не делай этого.


c11: 7.21.6 форматированные функции ввода / вывода (p8):

p аргумент должен быть указатель void. Значение указателя преобразуется в последовательность печатных знаков, в реализации манера.