Подтвердить что ты не робот

Printf и указатели

Возможный дубликат:
Исправить спецификатор формата для печати указателя (адреса)?

При печати указателя с помощью printf необходимо ли указывать указатель на void *? Другими словами, в коде типа

#include <stdio.h>
int main() {
    int a;
    printf("address of a = %p\n", &a);
}

должен ли аргумент действительно быть (void *) &a? gcc, похоже, не дает никаких предупреждений, когда явное приведение не производится.

4b9b3361

Ответ 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.