这段代码只是我在实际代码中发现的一个非常大的情况,所以我给出了这个.在此代码中,结构"struct node"未定义,它在另一个c源文件中定义.
我的源代码:
/* test.c */
1 #include<stdio.h>
2 #include "test2.h"
3
4 void f(struct node * k)
5 {
6
7 }
Run Code Online (Sandbox Code Playgroud)
我的头文件:
/* test2.h */
1 extern void f(struct node * k);
Run Code Online (Sandbox Code Playgroud)
当我用gcc编译这段代码来创建一个目标文件时:
gcc -w -c test.c
Run Code Online (Sandbox Code Playgroud)
我明白了:
test.c:6: error: conflicting types for 'f'
test2.h:1: error: previous declaration of 'f' was here
Run Code Online (Sandbox Code Playgroud)
我已经给出了完整的功能原型f().为什么我收到此错误?
另一件事是,当我没有在test.c中包含头文件 test2.h并显式声明函数原型时,它会成功编译.代码如下:test.c
/* test.c */
1 #include<stdio.h>
2 void f(struct node *k);
3
4 void f(struct node * k)
5 {
6
7 }
Run Code Online (Sandbox Code Playgroud)
gcc -c -w test.c
没错.
你能解释为什么这次我没有收到错误吗?
这与extern原型上的关键字无关(尽管没有必要).
你需要一个前瞻声明struct node:
/* test2.h */
struct node; // <== this
extern void f(struct node * k);
Run Code Online (Sandbox Code Playgroud)
如果没有前向声明,struct node函数原型内部就是特定函数原型的本地.
因此,当编译器看到f()它的定义时,也会看到另一个不同的本地声明,struct node并认为你有一个冲突的声明f().
见C99 6.9.1"标识符范围":
对于标识符指定的每个不同实体,标识符仅在称为其范围的程序文本的区域内可见(即,可以使用).由相同标识符指定的不同实体具有不同的范围,或者在不同的名称空间中.范围有四种:函数,文件,块和函数原型.(函数原型是声明其参数类型的函数的声明.)
...
如果声明标识符的声明符或类型说明符出现在函数原型(不是函数定义的一部分)的参数声明列表中,则标识符具有函数原型作用域,该作用域终止于函数声明符的末尾.
GCC 4.6.1为我提供了一个很好的警告:
C:\temp\test.c:3:22: warning: 'struct node' declared inside parameter list [enabled by default]
C:\temp\test.c:3:22: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
Run Code Online (Sandbox Code Playgroud)
C++使函数原型范围仅适用于参数名称(C++ 03 3.3.3),因此如果将代码编译为C++,则不会看到此问题.