在编写比较复杂的程序时,为了使程序看起来更加有条理,我们需要采取一些措施。之前我们已经学习了函数,但是有的时候只使用函数还是会看起来很混乱,这里我们就学习一下怎么使用多个.c文件来编写大程序。
1. 多个.c文件
main()里面的代码太长,适合分为几个函数;
一个源代码文件太长适合分为几个文件;
两个独立的源代码文件不能编译形成可执行的程序;
例1:多个.c文件的项目
首先新建一个项目,在源文件中新建MAIN.c以及max.c两个源文件。
对MAIN.c,程序代码为:
#include<stdio.h>
int max(int a,int b); //函数声明
int main(void)
{
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
对max.c,程序代码为:
int max(int a,int b)
{
return a>b?a:b;
}
编译单元:一个.c文件是一个编译单元,编译器每次只编译一个编译单元,然后再链接到一起。
2. 头文件
把函数原型放到一个头文件中,在需要调用这个函数的源代码文件中#include这个头文件,就能让编译器在编译的时候知道函数的原型。
例2:头文件
添加一个头文件为max.h。在max.h中写入代码:
int max(int a,int b);
删去main函数中的函数声明,并在MAIN.c和max.c写入:
#include “max.h”
#include是一个编译预处理指令,和宏一样,在编译之前就处理了。它会把那个文件的全部文本内容原封不动地插入到它所在的地方。
① 使用 ” ” 还是 < >
#include有两种形式来指出要插入的文件。
“”要求编译器首先在当前目录(.c文件所在目录)寻找这个文件,如果没有,到编译器指定的目录去找。
<>让编译器自己知道自己的标准库的头文件在哪里。
环境变量和编译器命令行参数也可以指定寻找头文件的目录。
综上,如果是自己写的头文件,使用””;如果是系统指定的头文件,使用<>。
② 使用头文件需要注意:
在使用和定义这个函数的地方都应该#include这个头文件;
一般的做法就是任何的.c文件都有对应同名的.h,把所有对外公开的原型和全局变量的声明都放进去。
③ 不对外公开的函数:
在函数前面加上static就使得它成为只能在所在的编译单元中被使用的函数;
在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量。
3. 声明
① 变量的声明
在max.c中有一个全局变量g,我们要在main.c里面也要访问到这个全局变量g,需要在.h文件声明全局变量。
int I;是变量的定义
extern int I;是变量的声明
② 声明和定义
声明是不产生代码的东西,包括:函数原型、变量声明、结构声明、宏声明、枚举声明、类型声明、inline函数。定义是会产生代码的。
③ 头文件
只有声明可以放到头文件中,否则会造成一个项目中多个编译单元有重名的实体。某些编译器允许几个编译单元存在同名的函数,或者用weak修饰符来强调这种存在。
对于结构声明,我们在代码比较多的情况下可能会出现重复声明的情况,为了避免这种情况,我们可以把结构声明放到.h文件中去。
④ 标准头文件结构
如果我们加入了两个头文件,在min.h中我们#include了max.h,这样的话我们可能会重复声明结构体,这种情况在程序很复杂的时候是不容易避免的,因此我们需要引入标准头文件结构。
#ifndef MAX_H //如果没有定义,定义过的话就跳过
#define MAX_H//进行定义
……
#endif
例3:声明
min.h文件:
#include "max.h"
max.h文件:
#ifndef _MAX_H_
#define _MAX_H_
int max(int a,int b);
extern int g;
struct Node{ //结构声明
int value;
char* name;
};
#endif
max.c文件:
#include "max.h"
int g=12;
int max(int a,int b)
{
return a>b?a:b;
}
MAIN.c文件:
#include<stdio.h>
#include "max.h"
#include "min.h"
int main(void)
{
int a=5;
int b=6;
printf("%d\n",max(a,g));
return 0;
}