Printf и указатели
Возможный дубликат:
Исправить спецификатор формата для печати указателя (адреса)?
При печати указателя с помощью printf
необходимо ли указывать указатель на void *
? Другими словами, в коде типа
#include <stdio.h>
int main() {
int a;
printf("address of a = %p\n", &a);
}
должен ли аргумент действительно быть (void *) &a
? gcc
, похоже, не дает никаких предупреждений, когда явное приведение не производится.
Ответы
Ответ 1
Да, приведение в void*
требуется.
int a;
printf("address of a = %p\n", &a);
&a
имеет тип int*
; printf "%p"
требуется аргумент типа void*
. Аргумент int*
неявно преобразован в void*
, поскольку объявление printf
не предоставляет информацию о типе для параметров, отличных от первого (строка формата). Все аргументы после строки формата имеют применяемые к ним аргументы по умолчанию; эти рекламные акции не преобразуют int*
в void*
.
Вероятный результат заключается в том, что printf
видит аргумент, который действительно имеет тип int*
, и интерпретирует его так, как будто он имеет тип void*
. Это тип-punning, а не преобразование, и он имеет поведение undefined. Вероятно, будет работать, если int*
и void*
имеют одно и то же представление, но языковой стандарт не гарантирует этого, даже подразумевая. И описанный мной тип - это только одно возможное поведение; стандарт говорит буквально ничего о том, что может случиться.
(Если вы делаете то же самое с невариантной функцией с видимым прототипом, поэтому компилятор знает в точке вызова, что параметр имеет тип void*
, тогда он будет генерировать код для неявного int*
-to- void*
. Это не так.)
Ответ 2
Является ли это вопросом C или С++? Для С++ кажется, что в соответствии с пунктом 5.2.2 [expr.call] не существует никакого неявного преобразования в void*
. Похоже, что пункт 6 C99 6.5.2.2 также не подразумевает какого-либо явного продвижения типов указателей. Это означало бы, что явное приведение к void*
требуется, поскольку типы указателей могут иметь разный размер (по крайней мере, на С++): если макет разных типов указателей не идентичен, вы получите поведение undefined. Может ли кто-нибудь указать, где гарантируется, что указатель передается с соответствующим размером при использовании списков переменных аргументов?
Конечно, будучи программистом на С++, это не проблема: просто не используйте функции с переменным числом аргументов. Однако это не жизнеспособный подход в C.
Ответ 3
Я думаю, что это может быть необходимо бросить. Мы уверены, что размер указателей всегда один и тот же? Я уверен, что недавно прочитал в stackoverflow, что размер (или, может быть, только выравнивание?) struct*
может отличаться от размера union*
. Это предполагает, что один или оба могут отличаться от размера a void*
.
Таким образом, даже если значение не сильно изменится или вообще не изменится при преобразовании, возможно, приведение необходимо для обеспечения правильности размера самого указателя.
В print
, %p
ожидает a void*
, поэтому вы должны явно его бросить. Если вы этого не сделаете, и если вам повезет, тогда размер указателя и представление указателя могут сохранить день. Но вы должны явно указать его на достоверность - что-то еще технически undefined.