Спецификатор формата% p нуждается в явном приведении в void * для всех типов, но char * в printf
Я прочитал много ответов об использовании спецификатора формата %p
на языке C здесь, в Stack Overflow, но ни один из них не дает объяснения, почему явный приведение в void*
необходимо для всех типов, но char*
.
Я, конечно, знаю о том, что это требование приведения к или из void*
связано с использованием вариационных функций (см. Первый комментарий этого ответа), в то время как необязательно в противном случае.
Вот пример:
int i;
printf ("%p", &i);
Выдает предупреждение о несовместимости типа и что &i
должен быть отброшен в void*
(как требуется стандартом, см. Здесь снова).
В то время как этот кусок кода компилируется плавно без каких-либо претензий на литье типов:
char * m = "Hello";
printf ("%p", m);
Как это происходит, когда char*
"освобождается" от этого императива?
PS: Возможно, стоит добавить, что я работаю над архитектурой x86_64, поскольку размер этого типа зависит от него, а с помощью gcc как компилятора на Linux с -W -Wall -std=c11 -pedantic
параметры компиляции.
Ответы
Ответ 1
Для аргументов типа char*
нет явного приведения, поскольку char *
имеет такое же требование представления и выравнивания, что и void *
.
Цитирование C11
, глава §6.2.5
Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. (48) [...]
и сноска 48)
Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращают значения из функций и членов профсоюзов.
Ответ 2
В стандарте C11 6.2.5/28 говорится:
Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. 48)
со сноской 48:
Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращают значения из функций и членов профсоюзов.
Однако 7.21.6.1 ("Функция fprintf") говорит о %p
:
Аргумент должен быть указателем на void.
Это, по-видимому, противоречие. На мой взгляд, разумная интерпретация заключается в том, что цель 6.2.5/28 состоит в том, что void *
и char *
на самом деле взаимозаменяемы как типы аргументов функции, которые не соответствуют прототипу. (т.е. аргументы для не-прототипированных функций или совпадение многоточия прототипа вариационной функции).
По-видимому, используемый вами компилятор использует аналогичный вид.
Чтобы поддержать это, спецификация типов аргументов в 7.21.6.1, если брать буквально без учета намерения, имеет множество других несоответствий, которые на практике не следует учитывать (например, это говорит, что printf("%lx", -1);
корректно определена, но printf("%u", 1);
- неопределенное поведение).
Ответ 3
Причиной этого требования является C-стандарт, позволяющий различать представления для указателей на разные типы, с двумя заметными ограничениями:
Следовательно, на некоторых архитектурах int *
и char *
могут иметь разные представления, например, другого размера, и они могут передаваться по-разному в функции vararg, вызывая int я = 1; printf("%p", &i);
int я = 1; printf("%p", &i);
и int я = 1; printf("%p", (void*)&i);
int я = 1; printf("%p", (void*)&i);
вести себя по-другому.
Обратите внимание, однако, что в стандартах Posix указано, что все типы указателей имеют одинаковый размер и представление. Следовательно, в системе Posix printf("%p", &i);
должны вести себя так, как ожидалось.
Ответ 4
Из стандарта С# 6.2.5p28
Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа.48) Аналогично, указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковые требования к представлению и выравниванию. Все указатели на типы структуры должны иметь одинаковые требования к представлению и согласованию друг с другом. Все указатели на типы объединения должны иметь одинаковые требования к представлению и согласованию друг с другом. Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию. [внимание мое]