C11 ISO标准(草案)第6.2.1(2)条将功能原型定义为(强调我的)
A function prototype is a declaration of a function that declares the types of its parameters.
所以声明如void(* f)(struct s {int c;});
不是函数的声明(它是指向函数的指针的声明),因此标记s具有文件或块作用域(取决于此声明出现的位置).那么似乎以下翻译单元void(* f)(struct s {int c;}); struct s a [42];
应该完全符合(抛开任何可用性问题).然而,gcc产生一个诊断’不完全类型的数组'(以及关于s的范围仅限于参数列表的警告),表明s具有函数原型范围,即使形式上没有函数声明.
标准的意图是说每个函数声明器中的参数列表定义了它自己的作用域(gcc和其他所有编译器似乎都解释了这个)?这个意图是否在标准的任何地方更正式地表达?任何尚未纳入标准的缺陷报告?
当然,这是一个语言律师问题,我很欣赏这种“偷偷摸摸”的标签声明是不好的风格.除了s在上面的声明中是不完整的类型之外,范围问题也使得用* f的原型定义函数变得不可能.最后,为了避免使用f本身的问题,可以将整个f的声明放在sizeof中,如int b [sizeof(void(*)(struct s {int c;}))];
最佳答案 在评论中进行长时间讨论的过程中,很明显这个问题实际上是关于§6.2.1/ 2中括号注释的确切含义,“函数原型是一个声明类型的函数的声明.它的参数.“
据我所知,“函数原型”这个短语没有正式的定义,它在标准中以各种方式使用.例如:
>§6.2.7/ 3兼容型和复合型:
If only one type is a function type with a parameter type list (a function prototype), the composite type is a function prototype with the parameter type list.
在这种情况下,“函数原型”的第一次使用显然不限于函数声明,因为它表示“函数类型” – 可能是在指向函数的指针的解析期间隐式创建的类型 – 并且“函数原型”的第二种用法可能根本没有语法表示,因为它是在复合类型算法过程中创建的类型
>§6.5.2.2函数调用
这里,第2段适用于具有“包含原型的类型”的函数,而第6段适用于具有“包含原型的类型”的函数的“不包括原型的类型”的函数.这些用法与上面“函数原型”的“定义”不兼容,因为该定义适用于整个函数类型,而“原型”的用法显然是在讨论函数类型的一部分.
>§6.7.6.3函数声明符(包括原型)
本节标题可以解释为关于所有“函数声明符”,包括碰巧是“原型”的那些,或者是关于“函数声明符”和可能包含的“原型”.
>§6.9.1/ 7函数定义
If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit.
这里的短语“函数原型”似乎是在谈论与函数类型相关或包含在函数类型中的东西,但函数声明符可以用作代理(“作为”).
我认为这不会超过像Principia Mathematica这样的作品的正式严格标准,但它与大多数法律规范和其他标准相同,其中需要一些常识来解释某些段落.
的确,有问题的引用:
“A function prototype is a declaration of a function that declares the types of its parameters.”
意味着参数列表中的参数是类型声明而不仅仅是规范(如果两个短语之间确实存在差异).
另外,如果我们解构§6.2.1/ 4(下面部分引用),我们会看到它说:
Every other identifier [that is, other than a label or macro name] has scope determined by the placement of its declaration (in a declarator or type specifier).
然后继续列出“声明标识符的声明符或类型说明符”的三个备选项:
>“出现在任何块或参数列表之外”
>“出现在块内”或“在参数声明列表中”
功能定义“
>“出现在函数原型的参数声明列表中(不是函数定义的一部分)”
假设这三种可能性是排他性的(因此规定不是有序的)和全面的(因为它们必须适用于“每个标识符”).然后,常识必须引导我们假设函数原型是一个函数声明器,它包含一个参数列表,或者(在某些上下文中)来自该语法结构的一些子集或语义抽象,所以你的第一个问题的答案是“是” ,这是标准的意图“.
这是否是标准中的缺陷是一个我不选择参与的意见问题.我检查了我认为是最新的清单](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2244.htm)并没有看到任何相关内容,因此据我所知它没有被报告为缺陷.
我的原始答案的某些版本如下,以防人们阅读评论.
标准对参数列表中标识符的范围所说的内容对我来说非常清楚. §6.2.1/ 4定义了参数列表中声明的标识符的两个可能范围(强调添加):
If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block.
If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. (§6.2.1/4)
在问题的例子中,struct的声明符出现一个参数列表,它恰好是“参数声明列表”.所以第二句适用,它们的范围仅限于参数列表本身.这使得struct无效,因此编译器会警告你.
如果结构类型说明符出现在定义的参数列表中,则它可以在函数体内使用,但是该结构的任何实例都不能从定义出现的转换单元传递给该函数.
我相信标准的意图是标记类型的类型说明符声明出现在参数列表之外. §6.7.2.3(标签)的语言加强了这一点:
- A specific type shall have its content defined at most once.
和
- Two declarations of structure, union, or enumerated types which are in different scopes or use different tags declare distinct types. Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.