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

时间:2016-01-14 16:27:35

标签: c function casting language-lawyer c11

此问题以某种方式与此one相关联。

我的上一个修订版显示,无法调用返回函数的数组的段落可能有一些实际用法。记住这个($ 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''。建设一个   返回类型的函数类型称为''函数类型   推导””。

     

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

正如我所见,没有禁止这样的限制:

void *ptr;

(int (*)()[4])ptr;

或者是吗?

1 个答案:

答案 0 :(得分:6)

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

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

6.5.2.2p1(函数调用):

  

表示被调用函数的表达式应具有类型   函数返回 void 或返回完整的指针   对象类型而不是数组类型。

和6.7.6.3p1(函数声明符):

  

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

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

强制转换操作符中的类型名称不是函数调用的一部分,也不是声明符,因此不适用约束。

因此,我相信这个计划:

int main(void) {
    if (0) {
        void *ptr;
        (int (*)()[4])ptr;
    }
}

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

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

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

我注意到gcc(带有-std=c11 -pedantic的版本5.3.0)拒绝带有消息的类型名称:

type name declared as function returning an array

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

暂时离开问题的主题,gcc也抱怨道:

 warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

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

更新:

Jens Gustedt的评论表明类型名称声明符,因此引用返回数组的函数的类型名称违反了6.7中的约束。 6.3p1。让我们按照N1570附录A中的语法并参考那里的章节数来看一下。

约束是指“函数声明符”。由于没有名为 function-declarator 的语法生成,因此它必须引用引用函数类型的声明符。如果没有声明者,则不违反约束。

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

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