转换为指向函数返回数组的指针 - 允许吗?

AnA*_*ons 2 c casting function language-lawyer c11

这个问题是在某种程度上与此挂钩一个.

我的上一个修订版显示,段落声明无法调用返回函数的数组可能有一些实际用法.记住这个($ 6.5.2.2.1):

表示被调用函数的表达式应具有指向函数的类型指针,该函数返回void或返回除数组类型之外的完整对象类型.

返回数组的函数的局限性仅涉及函数"声明符"和"定义".但是,如果我们查看强制转换操作符,则没有规则禁止"函数类型",它们返回数组以用作其中的"类型名称".

看看'$ 6.5.4.2':

6.5.4施法者

句法

投表达式:

 unary-expression 

 ( type-name ) cast-expression 
Run Code Online (Sandbox Code Playgroud)

约束

除非类型名称指定void类型,否则类型名称应指定原子,限定或非限定标量类型,操作数应具有标量类型.

现在,如果我们看看'$ 6.2.5.21':

21种运算类型和指针类型统称为标量类型.数组和结构类型统称为聚合类型.

然后在'$ 6.2.5.20':

- 函数类型描述具有指定返回类型的函数.函数类型的特征在于其返回类型以及其参数的数量和类型.函数类型据说是从它的返回类型派生的,如果它的返回类型是T,函数类型有时被称为''函数返回T''.从返回类型构造函数类型称为"函数类型派生".

- 指针类型可以从函数类型或对象类型派生,称为引用类型.指针类型描述一个对象,其值提供对引用类型的实体的引用.从引用类型T派生的指针类型有时被称为"指向T"的指针.从引用类型构造指针类型称为"指针类型派生".指针类型是完整的对象类型.

正如我所看到的那样,没有任何限制禁止这样的事情:

void *ptr;

(int (*)()[4])ptr;
Run Code Online (Sandbox Code Playgroud)

或者是吗?

Kei*_*son 6

我对此不太确定.请参阅Jens Gustedt的评论和我在本答案底部的不完整分析.

通常说C不允许返回数组的函数,但强制执行此限制的唯一约束是(引用N1570 C11草案):

6.5.2.2p1(函数调用):

表示被调用函数的表达式应具有指向函数的类型指针,该函数返回void或返回除数组类型之外的完整对象类型.

和6.7.6.3p1(函数声明符):

函数声明符不应指定函数类型或数组类型的返回类型.

(我在标准的第6部分中搜索了"约束"这个词.我认为我没有错过任何东西.如果我这样做,我相信有人会指出它.)

转换运算符中的类型名称不是函数调用的一部分,也不是声明符,因此两个约束都不适用.

结果,我相信这个计划:

int main(void) {
    if (0) {
        void *ptr;
        (int (*)()[4])ptr;
    }
}
Run Code Online (Sandbox Code Playgroud)

严格遵守,必须由符合要求的实施接受.(我添加了if (0)以避免任何有关转换的运行时语义的问题; void*通过省略来定义转换为函数指针的行为.)

这意味着,我认为只要不在函数调用或函数声明符中使用,就允许表示返回数组的函数的类型名称或返回函数的函数.例如,它可以用于泛型选择,in sizeof_Alignof表达式,以及其他几种上下文中.

当然,这没有用,而且可能只是委员会的疏忽.

我注意到gcc(版本5.3.0 with -std=c11 -pedantic)拒绝带有消息的type-name:

type name declared as function returning an array
Run Code Online (Sandbox Code Playgroud)

这似乎是一种合理的诊断,但严格来说它是不符合的,因为没有违反实际的约束.

一时间偏离问题的主题,gcc也抱怨道:

 warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
Run Code Online (Sandbox Code Playgroud)

这不是严格正确的.ISO C不禁止这种转换; 它只是没有定义它的行为.

更新:

Jens Gustedt的评论表明,type-name是一个声明符,因此引用返回数组的函数的类型名称违反了6.7.6.3p1中的约束.让我们看一下,按照N1570附录A中的语法并参考那里的章节编号.

约束是指"函数声明符".由于没有称为函数声明符的语法生成,因此必须引用引用函数类型的声明符.如果没有声明者,则不违反约束.

类型名称int (*)()[4],如果它有效,则引用一个指向函数的指针,返回一个4 int秒的数组(感谢cdecl).

我的分析不完整.我将不得不稍后回来.

  • 你查找了*type-name*的语法表吗?我认为那里的函数类型非常好的声明符.所以这个构造确实是一个约束违规. (2认同)