gcc在c99模式下是否正确执行隐式函数声明?

时间:2012-12-21 07:56:28

标签: gcc standards c99 c89

请考虑以下代码:

int main (void) {
    int i = xyzzy();
    return i;
}
int xyzzy (void) {
    return 42;
}

现在,尽管xyyzy的原型在使用时未知,但这在c89模式下工作,因为没有原型的函数的默认返回类型是int所以隐式函数原型和实际功能兼容。

事实上,如果你将函数的返回类型更改为float,你得到(正如预期的那样):

testprog.c:6: error: conflicting types for 'xyzzy'
testprog.c:2: error: previous implicit declaration of 'xyzzy' was here

因为隐式原型和实际函数不再匹配。

使用gcc --std=c89 --pedantic -Wall -Wextra编译的原始代码仅向我发出警告:

testprog.c: In function 'main':
testprog.c:2: warning: implicit declaration of function 'xyzzy'

这是预期的,因为c89在3.7.1 Function definitions中有这个说法:

  

extern int max(int a,int b){...}:这里extern是存储类说明符,int是类型说明符(每个其中可以省略,因为它们是默认值。)

3.3.2.2 Function calls

  

如果函数调用中带括号的参数列表之前的表达式仅包含一个标识符,并且如果此标识符没有可见的声明,则在包含最内层的块中隐式声明标识符。   函数调用,声明 extern int identifier(); 出现。

因此在声明肯定之前使用函数会导致创建默认原型。


但是,这两个短语都已在c99中删除,我们改为在6.5.2.2 Function calls(我的大胆)中找到:

  

如果表示被调用函数的表达式具有返回对象类型的函数的类型指针,则函数调用表达式与该对象类型具有相同的类型,并且具有在6.8.6.4中指定的值。 否则,函数调用的类型为void。

我理解这意味着,如果在尝试调用函数时视图中没有声明,则使用void返回类型隐式声明它。

然而,当使用gcc --std=c99 --pedantic -Wall -Wextra进行编译时,我得到了关于隐式声明的相同警告。

c99是否应该隐式声明该函数返回void?如果有的话,我会发现previous implicit declaration错误类似于我尝试将其重新声明为返回float时的错误。

gcc在这里被打破,或者我在标准中遗漏了什么?

2 个答案:

答案 0 :(得分:4)

您正在错误地阅读标准。在C中没有隐式函数声明这样的东西。它被C99从语言中删除。

当GCC看到一个看起来像隐式函数声明的错误构造时会发出警告。就标准而言,这是可以的。该标准需要诊断,警告是诊断。您可以使用-Werror=implicit-function-declaration标志将其转换为GCC。

答案 1 :(得分:1)

6.5.1主要表达

的说明中对此进行了介绍
  

2 - 标识符是主表达式,前提是它已被声明为指定   对象(在这种情况下它是一个左值)或一个函数(在这种情况下它是一个函数)   标志)。 79)

     
     

79)因此,未声明的标识符违反了语法。

5.1.1.3诊断需要符合要求的实现来生成诊断消息,以响应函数调用表达式的语法违规,该函数调用表达式涉及未声明的标识符作为表示被调用函数的表达式。当然可以自由地继续编译程序,就像标识符已经以隐式 - int C89样式声明一样。

必须参考约束6.5.2.2p1:

阅读第6.5.2.2p5段
  

1 - 表示被调用函数的表达式应具有指向函数的类型指针   返回void或返回数组类型以外的对象类型。

因此,如果“表示被调用函数的表达式的类型为“指向函数的指针返回对象类型”,它必须 ipso facto (通过约束6.5.2.2p1)具有类型“指向函数返回void ”的指针,并且在这种情况下“< em>其他“在6.5.2.2p5中涵盖。也就是说,我插入 [方括号]

  

5 - 如果表示被调用函数的表达式具有返回对象类型的函数的类型指针,则函数调用表达式与该对象类型具有相同的类型,并且具有在6.8.6.4中指定的值。 否则, [即如果表示被调用函数的表达式具有指向函数的类型指针,返回void,] ,则函数调用的类型为void

void需要使用特殊语言而不是对象类型;不是被调用函数表达式的许可证,或者包含未声明的标识符。