先上一个有多个源文件和头文件的C语言的代码程序:
/* main.c */ #include <stdio.h> #include "main.h" #include "stack.h" #include "maze.h" struct point predecessor[MAX_ROW][MAX_COL] = { {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, }; void visit(int row, int col, struct point pre) { struct point visit_point = { row, col }; maze[row][col] = 2; predecessor[row][col] = pre; push(visit_point); } int main(void) { struct point p = { 0, 0 }; maze[p.row][p.col] = 2; push(p); while (!is_empty()) { p = pop(); if (p.row == MAX_ROW - 1 /* goal */ && p.col == MAX_COL - 1) break; if (p.col+1 < MAX_COL /* right */ && maze[p.row][p.col+1] == 0) visit(p.row, p.col+1, p); if (p.row+1 < MAX_ROW /* down */ && maze[p.row+1][p.col] == 0) visit(p.row+1, p.col, p); if (p.col-1 >= 0 /* left */ && maze[p.row][p.col-1] == 0) visit(p.row, p.col-1, p); if (p.row-1 >= 0 /* up */ && maze[p.row-1][p.col] == 0) visit(p.row-1, p.col, p); print_maze(); } if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) { printf("(%d, %d)\n", p.row, p.col); while (predecessor[p.row][p.col].row != -1) { p = predecessor[p.row][p.col]; printf("(%d, %d)\n", p.row, p.col); } } else printf("No path!\n"); return 0; }
堆栈和迷宫的代码分别转移到模块stack.c
和maze.c
中,main.c
包含它们提供的头文件stack.h
和maze.h
/* main.h */ #ifndef MAIN_H #define MAIN_H typedef struct point { int row, col; } item_t; #define MAX_ROW 5 #define MAX_COL 5 #endif
在main.h
中定义了一个类型和两个常量,main.c
、stack.c
和maze.c
都要用到这些定义,都要包含这个头文件。下面是栈功能模块:
/* stack.c */ #include "stack.h" static item_t stack[512]; static int top = 0; void push(item_t p) { stack[top++] = p; } item_t pop(void) { return stack[--top]; } int is_empty(void) { return top == 0; }
/* stack.h */ #ifndef STACK_H #define STACK_H #include "main.h" /* provides definition for item_t */ extern void push(item_t); extern item_t pop(void); extern int is_empty(void); #endif
下面是迷宫功能模块:
/* maze.c */ #include <stdio.h> #include "maze.h" int maze[MAX_ROW][MAX_COL] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; void print_maze(void) { int i, j; for (i = 0; i < MAX_ROW; i++) { for (j = 0; j < MAX_COL; j++) printf("%d ", maze[i][j]); putchar(‘\n‘); } printf("*********\n"); }
/* maze.h */ #ifndef MAZE_H #define MAZE_H #include "main.h" /* provides defintion for MAX_ROW and MAX_COL */ extern int maze[MAX_ROW][MAX_COL]; void print_maze(void); #endif
这些源文件可以这样编译:
$ gcc -Wall -o main main.c stack.c maze.c
但这不是个好办法,如果编译之后又对maze.c
做了修改,又要把所有源文件编译一遍,即使main.c
、stack.c
和那些头文件都没有修改也要跟着重新编译。一个大型的软件项目往往由上千个源文件组成,全部编译一遍需要几个小时,只改一个源文件就要求全部重新编译肯定是不合理的。
这样编译也许更好一些:
$ gcc -c main.c $ gcc -c stack.c $ gcc -c maze.c $ gcc -o main main.o stack.o maze.o
如果编译之后又对maze.c
做了修改,要重新编译只需要做两步:
$ gcc -c maze.c $ gcc -o main main.o stack.o maze.o
但是这样,很容易出错,比如我修改了三个源文件,可能有一个忘了重新编译,结果编译完后运行代码,则忘记编译的那个源文件的修改没生效,运行时出了Bug还满世界找原因呢。更复杂的问题是,假如我改了main.h
怎么办?所有包含main.h
的源文件都需要重新编译,我得挨个找哪些源文件包含了main.h
,有的还很不明显,例如stack.c
包含了stack.h
,而后者包含了main.h
。可见手动处理这些问题非常容易出错,那有没有自动的解决办法呢?有,就是写一个Makefile
文件和源代码放在同一个目录下:
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main main.o: main.c main.h stack.h maze.h gcc -c main.c stack.o: stack.c stack.h main.h gcc -c stack.c maze.o: maze.c maze.h main.h gcc -c maze.c
然后在这个目录下运行make
编译:
$ make gcc -c main.c gcc -c stack.c gcc -c maze.c gcc main.o stack.o maze.o -o main
make
命令会自动读取当前目录下的Makefile
文件,完成相应的编译步骤。Makefile由一组规则(Rule)组成,每条规则的格式是:
target ... : prerequisites ... command1 command2 ...
例如:
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main
main
是这条规则的目标(Target),main.o
、stack.o
和maze.o
是这条规则的条件(Prerequisite)。目标和条件之间的关系是:欲更新目标,必须首先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新。所谓“更新”就是执行一遍规则中的命令列表。命令列表中的每条命令必须以一个Tab开头,注意不能是空格,Makefile的格式不像C语言的缩进那么随意,对于Makefile中的每个以Tab开头的命令,make
会创建一个Shell进程去执行命令列表。
对于上面这个例子,make
执行如下步骤:
尝试更新Makefile中第一条规则的目标main
,第一条规则的目标称为缺省目标,只要缺省目标更新了就算完成任务了,其它工作都是为这个目的而做的。由于我们是第一次编译,main
文件还没生成,显然需要更新,但规则说必须先更新了main.o
、stack.o
和maze.o
这三个条件,然后才能更新main
。
所以make
会进一步查找以这三个条件为目标的规则,这些目标文件也没有生成,也需要更新,所以执行相应的命令(gcc -c main.c
、gcc -c stack.c
和gcc -c maze.c
)更新它们。
最后执行gcc main.o stack.o maze.o -o main
更新main
。
如果没有做任何改动,再次运行make
:
$ make make: `main‘ is up to date.
make
会提示缺省目标已经是最新的了,不需要执行任何命令更新它。
再做个实验,如果修改了maze.h
(比如加个无关痛痒的空格)再运行make
:
$ make gcc -c main.c gcc -c maze.c gcc main.o stack.o maze.o -o main
make
会自动选择那些受影响的源文件重新编译,不受影响的源文件则不重新编译,这是怎么做到的呢?
make
仍然尝试更新缺省目标,首先检查目标main
是否需要更新,这就要检查三个条件main.o
、stack.o
和maze.o
是否需要更新。
make
会进一步查找以这三个条件为目标的规则,然后发现main.o
和maze.o
需要更新,因为它们都有一个条件是maze.h
,而这个文件的修改时间比main.o
和maze.o
晚,所以执行相应的命令更新main.o
和maze.o
。
既然main
的三个条件中有两个被更新过了,那么main
也需要更新,所以执行命令gcc main.o stack.o maze.o -o main
更新main
。
注意:这种更新的检查都要递归到源码文件或者头文件。
通常Makefile都会有一个clean
规则,用于清除编译过程中产生的二进制文件,保留源文件:
原文:http://www.cnblogs.com/stemon/p/5161593.html