为何ANSI C语法允许`int func()()`这类语法合法但语义非法的声明?
int func()()这类语义非法但语法合法的声明? 这个问题问到了C语言语法设计里一个很有意思的点——语法合法性和语义合法性的分离,咱们一步步拆解来看:
首先先明确题目中给出的ANSI C declarator语法规则:
declarator: pointer_opt direct-declarator direct-declarator: identifier ( declarator ) direct-declarator [ constant-expression_opt ] direct-declarator ( parameter-type-list ) direct-declarator ( identifier-list_opt )
按照这套规则,func()()确实可以被合法推导为一个declarator:
- 第一步:
func是identifier,属于direct-declarator;给它加上空参数列表()(对应规则里的direct-declarator ( identifier-list_opt )),得到新的direct-declarator:func()。 - 第二步:把这个
func()作为新的direct-declarator,再套一层空参数列表(),就得到了func()(),完全符合语法规则,因此可以和类型说明符int组合成int func()()这样的声明。
那为什么ANSI C的语法要允许这种语义上非法的结构存在?主要有这几个核心原因:
语法设计的正交性与简洁性
C的语法规则追求正交性——让各个语法元素可以自由组合,而不是为了提前排除语义错误就给规则加一堆特殊约束。如果要在语法层面禁止“函数返回函数”这种情况,就得给direct-declarator ( ... )的规则额外加检查:判断前面的direct-declarator是不是函数类型。这会让语法规则变得臃肿复杂,大幅增加编译器语法分析阶段的实现难度。语法检查与语义检查的职责分离
C编译器的处理是分阶段的:语法分析只负责验证代码的结构是否符合语法规则,而语义分析才会检查类型是否合法、操作是否有意义。把“函数不能返回函数”这种语义限制放到语义阶段处理,既符合编译器的分工逻辑,也让语法规则保持清晰易懂——语法只管“结构对不对”,语义只管“逻辑合不合理”。历史兼容性考量
ANSI C是在早期C语言的基础上标准化的,早期C的语法规则更为宽松,允许很多语法层面的自由组合。标准化时为了兼容大量已有的旧代码,并没有过度收紧语法规则,而是把这类语义错误的检查交给编译器的后续阶段去处理。
顺便提一句,虽然int func()()语义非法,但类似的写法如果是返回函数指针就是合法的——比如int (*func())(),这表示一个返回int (*)()类型(指向返回int的函数的指针)的函数,是完全符合C语言语义的。
内容的提问来源于stack exchange,提问作者Sinushyperbolikus

