Приведение к указателю на функцию, возвращающую массив - это разрешено?
Этот вопрос как-то связан с этим.
Моя последняя ревизия показывает, что параграф, утверждающий, что массивы, возвращающие функции, не могут быть вызваны, может иметь некоторое фактическое использование. Запомните это ($6.5.2.2.1):
Выражение, которое обозначает вызываемую функцию, должно иметь указатель типа на функцию, возвращающую void или возвращающую полный тип объекта, отличный от типа массива.
Ограничения функций, возвращающих массивы, касаются только функций "объявлений" и "определений". Однако, если мы посмотрим на оператор приведения, нет правила, запрещающего "типы функций", которые возвращают массивы для использования в качестве "имени типа".
Посмотрите на '$ 6.5.4.2':
6.5.4 Операторы приведения
Синтаксис
монолитно-выражение:
unary-expression ( type-name ) cast-expression
Ограничения
Если в имени типа не указан тип void, имя типа должно указывать атомарный, квалифицированный или неквалифицированный скалярный тип, а операнд должен иметь скалярный тип.
Теперь, если мы посмотрим на "$6.2.5.21":
21 Арифметические типы и типы указателей вместе называются скалярными типами. Типы массива и структуры вместе называются агрегатными типами.
А затем на уровне "$6.2.5.20":
- Тип функции описывает функцию с указанным типом возврата. Тип функции характеризуется типом возвращаемого значения, а также количеством и типами его параметров. Считается, что тип функции является производным от ее возвращаемого типа, и если его возвращаемый тип равен T, то этот тип функции иногда называют "функцией, возвращающей T". Конструкция типа функции из возвращаемого типа называется "деривацией типа функции".
- Тип указателя может быть получен из типа функции или типа объекта, называемого ссылочным типом. Тип указателя описывает объект, значение которого предоставляет ссылку на объект ссылочного типа. Тип указателя, полученный из ссылочного типа T, иногда называют "указателем на T". Конструкция типа указателя из ссылочного типа называется "выводом типа указателя". Тип указателя является полным типом объекта.
Как я вижу, нет ограничения, которое запрещает что-то вроде этого:
void *ptr;
(int (*)()[4])ptr;
Либо это?
1 ответ
Я менее уверен в этом, чем был. См. Комментарий Йенса Гастедта и мой неполный анализ внизу этого ответа.
Обычно говорят, что C не разрешает функции, которые возвращают массивы, но единственными ограничениями, которые обеспечивают это ограничение, являются (цитируя черновик N1570 C11):
6.5.2.2p1 (вызовы функций):
Выражение, которое обозначает вызываемую функцию, должно иметь указатель типа на функцию, возвращающую
void
или возвращая полный тип объекта, отличный от типа массива.
и 6.7.6.3p1 (объявления функций):
Декларатор функции не должен указывать тип возврата, который является типом функции или типом массива.
(Я искал слово "Ограничения" в разделе 6 стандарта. Я не думаю, что что-то пропустил. Если я это сделал, я уверен, что кто-то укажет на это.)
Имя типа в операторе приведения не является частью вызова функции и не является декларатором, поэтому ни одно из ограничений не применяется.
В результате я считаю, что эта программа:
int main(void) {
if (0) {
void *ptr;
(int (*)()[4])ptr;
}
}
строго соответствует и должно быть принято соответствующей реализацией. (Я добавил if (0)
избегать любых проблем, связанных с семантикой преобразования во время выполнения; поведение преобразования void*
указатель на функцию не определяется пропуском.)
Я думаю, это означает, что имя типа, обозначающее функцию, возвращающую массив, или функцию, возвращающую функцию, разрешено, если оно не используется в вызове функции или в объявлении функции. Например, он может быть использован в общем выборе, в sizeof
или же _Alignof
выражение, и в нескольких других контекстах.
Это, конечно, бесполезно, и, вероятно, это просто недосмотр со стороны комитета.
Замечу, что gcc (версия 5.3.0 с -std=c11 -pedantic
) отклоняет имя типа с сообщением:
type name declared as function returning an array
Это кажется разумной диагностикой, но, строго говоря, это не соответствует требованиям, поскольку фактическое ограничение не нарушается.
Отказываясь от темы вопроса на мгновение, gcc также жалуется:
warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
что не совсем правильно. ISO C не запрещает такое преобразование; это просто не определяет его поведение.
ОБНОВИТЬ:
Комментарий Дженса Гастта предполагает, что имя типа является декларатором, и поэтому имя типа, ссылающееся на функцию, возвращающую массив, нарушает ограничение в 6.7.6.3p1. Давайте посмотрим на это, следуя грамматике в Приложении A N1570 и ссылаясь на номера разделов там.
Ограничение ссылается на "декларатор функции". Поскольку нет грамматического производства, называемого функцией-декларатором, оно должно ссылаться на декларатор, который ссылается на тип функции. Если декларатора нет, то ограничение не нарушается.
Имя типа int (*)()[4]
если он действителен, ссылается на указатель на функцию, возвращающую массив из 4 int
с (спасибо cdecl
).
Мой анализ неполон. Я должен вернуться к этому позже.