很多初学C语言的同学往往一个程序就是一个文件,好一点的只有一个.h文件。甚至有的初学者会把函数放到一个.c文件,然后在main.c中include那个.c文件 ==(我就干过这种事)。下面雅乐网说一下多文件编程方面自己的经验(菜鸟教程)。
1. include的作用
把C语言源代码变成可执行文件的时候,编译器的第一步会进行预处理,把include的文件的内容全部复制到本文件。
比如我们有一个header.h文件内容如下
1 2 3 4 5 6 7 |
#define MAX_NAME_LEN 30 struct student { int id; char name[MAX_NAME_LEN]; }; |
我们还有一个main.c文件
1 2 3 4 5 6 7 |
#include "header.h" int main(void) { return 0; } |
我们可以使用gcc的-E选项来对一个文件进行预处理,
可以看到预处理后的内容是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 1 "main.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "main.c" # 1 "header.h" 1 struct student { int id; char name[30]; }; # 2 "main.c" 2 int main(void) { return 0; } |
可以看出预处理的时候会把include的文件的内容全部复制过来。
2. 一个文件的简单程序
我们有一个学生信息处理程序,只有一个student结构体(在上面header.h里的一样),和三个函数改变id的 change_id(struct student * p, int id)和改变姓名的change_name(struct student* p, char * pchar)。该程序现在是这个样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
#include <stdio.h> #define BOOL int #define TRUE 1 #define FALSE 0 #define MAX_NAME_LEN 30 struct student { int id; char name[MAX_NAME_LEN]; }; BOOL change_id(struct student * p, int id); BOOL check_id (int id); void change_name(struct student * p, char * pchar); int main(void) { int op = 1; int id; char name[MAX_NAME_LEN]; struct student zhangsan = {1, "lisi"}; while (op != 0) { printf("0. exit\n"); printf("1. change_id\n"); printf("2. change_name\n"); scanf("%d", &op); switch(op) { case 1: { printf("input id: "); scanf("%d", &id); if (change_id(&zhangsan, id) == TRUE) ; else printf("id is not corrext\n"); break; } case 2: { printf("input name\n"); scanf("%s", name); change_name(&zhangsan, name); break; } default: { break; } } printf("now zhangsan\'s id is %d.\n", zhangsan.id); printf("and zhangsan\'s name is %s.\n", zhangsan.name); } return 0; } BOOL change_id(struct student * p, int id) { if (check_id(id) == FALSE) { return FALSE; } else { p->id = id; return TRUE; } } BOOL check_id (int id) { if (id > 100) { return FALSE; } else { return TRUE; } } void change_name(struct student * p, char * pchar) { int i; for (i = 0; pchar[i] != '\0' && i < MAX_NAME_LEN; ++i) { p->name[i] = pchar[i]; } } |
3. 拆分成多文件
上面这个程序只是为了说明怎么变成多文件,不要吐槽程序的功能……
分模块
首先,我们想把change_id做成一个模块,把change_name做成一个模块。
C语言中一般一个模块由一个.c文件和一个同名的.h文件组成。其中,.h文件用来描述该模块的接口,也就是函数,用来存放声明,而不能放定义。.c文件放具体的函数定义。而函数的声明就放在.h文件里。
首先我新建四个文件 change_id.h change_id.c change_name.h change_name.c
然后把相应的函数分别复制过去。其中check_id是在change_id里面用的,我们把它们放到一起。现在程序变成了这样
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include <stdio.h> #define BOOL int #define TRUE 1 #define FALSE 0 #define MAX_NAME_LEN 30 struct student { int id; char name[MAX_NAME_LEN]; }; int main(void) { int op = 1; int id; char name[MAX_NAME_LEN]; struct student zhangsan = {1, "lisi"}; while (op != 0) { printf("0. exit\n"); printf("1. change_id\n"); printf("2. change_name\n"); scanf("%d", &op); switch(op) { case 1: { printf("input id: "); scanf("%d", &id); if (change_id(&zhangsan, id) == TRUE) ; else printf("id is not corrext\n"); break; } case 2: { printf("input name\n"); scanf("%s", name); change_name(&zhangsan, name); break; } default: { break; } } printf("now zhangsan\'s id is %d.\n", zhangsan.id); printf("and zhangsan\'s name is %s.\n", zhangsan.name); } return 0; } |
其余四个文件
公用结构体
由于changeid和changename模块都用到了struct student类型的结构体,必须在每个.c中声明。下面我们把声明写到一个公用头文件里header.h
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <stdio.h> #define BOOL int #define TRUE 1 #define FALSE 0 #define MAX_NAME_LEN 30 struct student { int id; char name[MAX_NAME_LEN]; }; |
然后在main.c change_id.c change_name.c开头部分均添加#include “header.h”
在mian.c中添加
#include “change_id.h”
#include “change_name.h”
就可以了。不过现在由于是多文件,编译链接的时候需要指定多个文件。我们也可以使用IDE把这些文件都添加进工程
现在各个文件内容如下
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include "header.h" #include "change_id.h" #include "change_name.h" int main(void) { int op = 1; int id; char name[MAX_NAME_LEN]; struct student zhangsan = {1, "lisi"}; while (op != 0) { printf("0. exit\n"); printf("1. change_id\n"); printf("2. change_name\n"); scanf("%d", &op); switch(op) { case 1: { printf("input id: "); scanf("%d", &id); if (change_id(&zhangsan, id) == TRUE) ; else printf("id is not corrext\n"); break; } case 2: { printf("input name\n"); scanf("%s", name); change_name(&zhangsan, name); break; } default: { break; } } printf("now zhangsan\'s id is %d.\n", zhangsan.id); printf("and zhangsan\'s name is %s.\n", zhangsan.name); } return 0; } |
header.h
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <stdio.h> #define BOOL int #define TRUE 1 #define FALSE 0 #define MAX_NAME_LEN 30 struct student { int id; char name[MAX_NAME_LEN]; }; |
change_id.h
1 2 |
BOOL change_id(struct student * p, int id); BOOL check_id (int id); |
change_id.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "header.h" BOOL check_id (int id) { if (id > 100) { return FALSE; } else { return TRUE; } } BOOL change_id(struct student * p, int id) { if (check_id(id) == FALSE) { return FALSE; } else { p->id = id; return TRUE; } } |
change_name.h
1 |
void change_name(struct student * p, char * pchar); |
change_name.c
1 2 3 4 5 6 7 8 9 10 |
#include "header.h" void change_name(struct student * p, char * pchar) { int i; for (i = 0; pchar[i] != '\0' && i < MAX_NAME_LEN; ++i) { p->name[i] = pchar[i]; } } |
函数作用域
只在本文件中用到的函数可以在.c的函数前面用static修饰,这样函数作用于就是本文件了。如果一个.c的函数需要在其他.c中使用,只需要去掉static 然后再需要使用的地方再用extern 声明。
例如我们想在change_name.c中使用check_id函数,只需要在change_name上面声明 extern BOOL check_id (int id); 就可以啦