最早的C是没有声明的,只有定义,声明主要是提供给连接器(link)用的,现在好像C编译器也用到声明。(这句话我不太确定,你当作耳旁风好了)

定义就是给出一个函数的明确功能,比如:

1
2
3
4
5
int foo()
{
  int i;
  return i * i; 
}

在一个C的工程中,不允许出现同名的函数,说得更清楚点就是,一个函数只有一个定义(define)。这样做很容易理解,比如有两个不同实现的foo()函数,那么再某个调用的foo()的地方究竟应该跳转到何处呢?不得不说的是:ANSI C规定同一个C工程中一个函数只能有一个定义。(C++允许函数重载不在此讨论范围内)但是某些具体的C编译器允许一个函数在同一个工程的不同模块中有相同的定义,当然搂虽然内容相同的定义影响不大,但是…ANSI做事总不会错!^_^

声明(declare)是什么?声明就是告诉C编译器这儿有个函数,已经定义了。声明之后的代码就可以根据声明提供的信息去调用这个函数。声明的写法如下:

1
2
3
int foo();

extern int foo();

一般来说,在一个C文件里面,我们会把声明写在所有函数定义的上头,这样所有的函数定义中就可以用这些函数了,而不必考虑每个函数定义书写顺序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//declare foo() and bar()
void foo();
void bar();

//define foo()
void foo()
{
  bar(); //As declare bar() before, so foo()can call bar()
}

void bar()
{
  . //do some thing
}

如果没有开头的bar()的声明,那么在foo()中调用bar()就是不允许的,因为foo()找不到bar()。

当然更规矩的写法是,是把declare写到与.c同名的head file里面,一般是.h, 在.c里面写对应函数的define。

如果bar()函数在我们的模块根本没有实现,bar()是由第三方提供的,我们要用 bar()的功能,但是我们目前还没有拿到bar()的代码,怎么办?用extern就可以搞定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//declare extern function
extern void bar();

//declare function
void foo();

//define function 
void foo()
{
  bar(); //no problem to compile our module without define bar()
}

所以void bar() 和extern void bar()的区别就是:虽然两者都是全局(globle scope)的函数,但是如果没有extern 在编译的时侯势必要要求提供bar()的define,而加了extern之后就可以把查找bar的define的工作推迟到link的时侯进行。在link的时侯,会为extern的函数声明找到一个定义,如果出现了重复定义,或者定义和声明不一致,都会出现link错误。

值得一提的是,某些编译器把没加extern的函数声明也当作加了extern处理。

说到这里,顺便说说static了。static如果用作函数的定义,则说明当前函数的作用范围只在当前文件内,外面的文件看不到,编译成obj时也不会放在全局符号表里。当然,static也可以作为函数声明的限定语。他用来说明当前使用的这个函数,是在本文件中定义的。有个容易混淆的地方是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//declare
static void bar();// 无论这里是用extern修饰,还是用static修饰,还是没有任何修饰,结果都一样
void foo();

//define 
void foo()
{
  bar(); //call bar() defined below
}

 static void bar()
{
   //do some thing
}

即便是外部定义了bar(),foo()调用也是本文件定义的bar()。因为在编译当前.c文件时,在本文件中已经找到了bar(),就没有必要再去外面找bar()的定义;只有在当前.c文件中找不到bar()的定义,才会去外面找bar()。