Является ли следующий код действительным C? (божественная стрела)
typedef struct none none;
none f(none, none);
Для ясности: идентификатор f
никогда не появляется в единице перевода.
снова и сама функция никогда не определена, даже в другом
единица перевода.
@sergeyrar Я не спрашиваю, принимают ли определенные компиляторы код как есть. Некоторые делают, некоторые нет. Я спрашиваю, действительно ли это с точки зрения стандарта. Обратите внимание на тег language-lawyer
.
Из любопытства, какие компиляторы это принимают и не принимают? Вы уверены, что все они намерены соответствовать одному и тому же стандарту? Я еще не нашел ни одного, кто не принимает это.
@NateEldredge gcc, clang и другие принимают код. cparser (git, а не на godbolt) не принимает его. Он жалуется на неполный тип параметра: cparser: «ошибка: анонимный параметр имеет неполный тип».
Интересно: GCC 10,2.0, работающий на RHEL 7.4, принимает код даже с опциями gcc -pedantic -pedantic-errors -Wall -Werror -Wextra -std=c18 …
. Если вы пытались использовать или реализовать функцию в исходном файле, то ошибка правильная — и GCC ее замечает. Я думаю, что есть основания утверждать, что cparser
правильно, и GCC имеет недосмотр. Однако я не уверен, что соответствующая программа сможет обнаружить проблему — это не соответствующий код, если он не будет компилироваться в соответствующем компиляторе.
Стандарт C17 прямо говорит, что параметры могут иметь неполные типы:
(6.7.6.3 (12)) Если декларатор функции не является частью определения этой функции, параметры могут быть неполными. тип и могут использовать нотацию
[*]
в своих последовательностях спецификаторов декларатора для указания переменной типы массивов длины.
Таким образом, cparser неправильно отклоняет код на этом основании.
Что касается типов возвращаемых значений, в любом случае нет четкого утверждения. cppreference говорит: «Тип возвращаемого значения функции [...] должен быть полным типом объекта, не являющимся массивом, или типом void», но я не могу найти соответствующее требование в стандарте. В стандарте 6.9.1 (3) в разделе «Определения функций» говорится, что «тип возвращаемого значения функции должен быть void
или полным типом объекта, отличным от типа массива», но я прочитал, что это относится только к определениям. Аналогично, 6.5.2.2(1) требует, чтобы вызываемая функция имела полный тип возвращаемого значения или void
.
Поэтому я считаю, что неполные возвращаемые типы разрешены в объявлениях, пока функция не определена или не вызвана. Но трудно быть уверенным.
«[...] 6.9.1 (3) [...] но я прочитал, что это относится только к определениям». Я тоже так читал, но тогда валидность кода зависит от другого неизвестного для меня: нужно ли объявление функции в конечном определении (возможно, в другой TU) или нет?
@nebel: я думаю, что нет. Функция f
объявлена с внешней связью, а в 6.9(5) говорится: «Если идентификатор, объявленный с внешней связью, используется в выражении (кроме как часть операнда оператора sizeof или _Alignof, результатом которого является целочисленная константа), где-то во всей программе должно быть ровно одно внешнее определение идентификатора, иначе их должно быть не более одного». Здесь мы находимся в случае «иначе», поэтому разрешено нулевое определение.
Вы пробовали его скомпилировать? (Если скомпилировано - валидно, иначе - не валидно)