--C语言的要求:函数先声明后定义
--example 1: test1.c中
#include<stdio.h>
int main(void)
{
printf("%d\n",fun(5));
}
int fun()
{
printf("Hello World\n");
}
//代码编译,链接都能够通过。但是会有警告:
warning C4013: 'fun' undefined; assuming extern returning int
warning C4716: 'fun' : must return a value
通过可以看出虽然fun(5)在使用前没有声明,但是编译器仅仅是给出警告,并提供默认的函数声明类型:int name(void); (参考C语言创始人的书),编译器在遇到 fun(5) 的“fun(”的时候就假设fun是一个函数,如果没有找到fun函数,就提供默认的函数声明,且忽略fun中的函数参数。并在链接的过程中找到了fun函数的定义,则通过链接,生成可执行代码。如下代码,能够通过编译,却不能通过链接,因为链接的时候找不到fun函数的定义。
#include<stdio.h>
int main(void)
{
printf("%d\n",fun(5));
}
--注:第一个程序在C++编译器里不能通过编译,因为C++编译器严格要求函数先声明后使用,如果找不到声明,不能通过编译,编译器并不会提供默认的函数声明。
--多文件组织中函数的先声明后定义
--test1.c
#include<stdio.h>
int main(void)
{
printf("%d\n",fun(5));
}
--test2.c
int fun()
{
printf("Hello World\n");
}
--上述文件在编译时能够通过编译,并且能偶通过链接,但是会产生如下警告:
Test1.c : warning C4013: 'fun' undefined; assuming extern returning int
Test2.c:
warning C4013: 'printf' undefined; assuming extern returning int
warning C4716: 'fun' : must return a value
test1.c中产生警告的原因是上面已经解释过了,链接能够通过是链接器能够自动的把一起编译的test1.c和test2.c中可执行代码链接起来,这样test1.c中的fun函数就找到了定义。
Test2.c中第一个警告是由于上述同样的原因,能够通过链接是由于test1.c中已经包含了头文件<stdio.h>,把printf的可执行代码给链接过来了,所以test2.c中的printf也就能够找到定义。
--上述例子说明多个源文件文件在编译环境下可以通过链接器自动连接到一起并不需要头文件的参与,但是为了程序的可移植性和正确性,一定要遵循先声明后使用的原则:这个原则不但适用于函数,也使用全局变量。
--头文件中应该包含什么
--test.h
int a;
void show(void)
{
printf("Hello world\n");
}
--test1.c
int fun()
{
printf("Hello World\n");
}
--test2.c
#include<stdio.h>
#include "test.h"
int main(void)
{
printf("%d\n",fun(5));
}
上述编译和链接同样能够通过,通过上面的例子不难理解:因为就是在头文件中定义了全局变量,并把它包含在test2.c中;
--test.h
int a;
void show(void)
{
printf("Hello world\n");
}
--test1.c
#include “test.h ”
int fun()
{
printf("Hello World\n");
}
--test2.c
#include<stdio.h>
#include "test.h"
int main(void)
{
printf("%d\n",fun(5));
}
--上述多文件程序能够通过编译,却不能通过链接:
error LNK2005: _show already defined in test1.obj
fatal error LNK1169: one or more multiply defined symbols found
具体原因有以下两个:
1:test1.c包含了test.h的头文件,定义了全局变量 a,同理test.2也通过相同的手段定义了全局变量a;
2: test1.c和test2.c通过上述手段分别提供了show的定义
--上述分析指出在链接的过程中由于出现了同一个函数和变量的多个定义,所以链接出错。
所以在头文件不要包含函数的定义和全局变量的定义,这样做虽然有时候链接和编译都能通过,但是还是有一定的隐患。
总结,在头文件中一般只包含函数的声明和全局变量的声明。函数的声明大家都知道,全局变量的声明大家不一定清楚:必须显式的提供extern
如:extern int a; 注意在头文件中这样只是声明,并不提供实际的定义,在使用该变量的源文件中还有重新以 int a ; 的形式重新定义,而其他使用该全局变量的源文件只需包含该头文件即可。