对于多文件工程,可以有两种方法编译:1 手动编译每个工程文件,然后生成最后的可执行文件。
2 编写Makefile文件(几种方法)
工程例子:创建一个目录为GCC,该目录下创建一个main.c文件和两个文件夹分别是add和sub。add目录下创建add.c和add.h文件,sub目录下创建sub.c和sub.h文件。目录结构如下图所示:
main.c文件代码如下:
#include<stdio.h>
#include<add.h>
#include<sub.h>
int main()
{
int a = 1,b = 2;
printf("int a+b iS:%d\n",add(a,b));
printf("int a-b iS:%d\n",sub(a,b));
return 0;
}
add.c文件代码如下:
int add(int a,int b)
{
return a+b;
}
add.h文件代码如下:
#ifdef _ADD_H_
#define _ADD_H_
extern int add(int a,int b);
#endif/*_ADD_H_*/
sub.c文件代码如下:
int sub(int a,int b)
{
return a-b;
}
sub.h文件代码如下:
#ifdef _SUB_H_
#define _SUB_H_
extern sun(int a,int b);
#endif /*_SUB_H*/
1 多工程文件的第一种编译方法:命令行编译
命名最终生成的可执行文件为out,执行如下命令,生成add.o,sub.o和main.o
然后将以上*.o文件链接成out文件,执行如下命令:
最后生成可执行文件:
2 用Makefile文件方式编译(Makefile文件的几种写法)
Makefile的规则
A 、Makefile的框架是由规则构成的,make命令执行时先在Makefile文件中查找各种规则,对各种规则进行解析后,运行规则。规则的基本格式为:
目标... : 依赖...
<TAB>命令
......
.......
目标(TARGET):规则所定义的目标。如上例所示的out。
依赖(DEPENDEDS):执行此规则所必须的依赖条件,如上例所示的add.o,sub.o,main.o文件链接成可执行文件out,*.o文件是依赖也是材料,out是目标。
<TAB>命令(COMMAND):规则所需要执行的命令。如上例所示的gcc - add/add.c -o add/add.o等命令。在此规则需要注意的是,命令前必须是tab键,tab键告诉make这是一个命令行,make执行相应的动作。
B、规则的嵌套。规则之间是可以嵌套的,如上例所示,生成out可执行文件需要很多的*.o文件,而每个.o文件又分别是一个规则,他们有自己的依赖,比如add.o的依赖是add.c。必须先根据规则实现这些*.o文件,然后再根据规则实现可执行文件。
C、 文件的时间戳。make命令执行的时候会根据文件的时间错判定是否执行相关的命令,如对main.c文件进行修改后保存,文件的生成日期就发生了改变,再次用make命令编译的时候,就只会编译main.c,add.c没有变化就不知重新编译,然后连接程序生成out可执行文件。
1)makefile的第一种写法
#生成out的规则
out:main.o add.o sub.o
gcc -o out main.o add/add.o sub/sub.o
#生成main.o的规则
main.o:main.c add/add.h sub/sub.h
gcc -c -o main.o main.c -Iadd -Isub
#生成add.o的规则
add.o:add/add.c add/add.h
gcc -c -o add/add.o add/add.c
#生成sub.o的规则
sub.o:sub/sub.c sub/sub.h
gcc -c -o sub/sub.o sub/sub.c
#清理的规则
clean:
rm -f out main.o sub.o add.o
执行make命令(警告忽略):
以上可以看出是规则的嵌套,先生成add.o sub.o main.o,最后生成了可执行文件out。
2)makefile的第二种写法(用户自定义变量)
使用Makefile进行规则定义的时候,用户可以定义自己的变量。
用OBJS变量表示可执行文件out的依赖文件,CC变量表示gcc,用CFLAGS表示编译的选项,RM表示rm -f,TARGET表示最终的生成目标out(在调用上面这些变量的时候在其前面加上$)。即
OBJS = main.o add.o sub.o
CC = gcc
CFLAGS = -Isub -Iadd
TARGET = out
RM = rm -f
在Makefile中有一些已经定义的变量,用户可以直接使用这些变量,不进行定义,在进行编译的时候,某些条件下的Makefile会使用这些预定义变量的值进行编译,经常采用过的有如下图所示:
在Makefile中经常用变量CC来表示编译器,其默认值为cc,即使用cc命令来进行编译C程序,在进行程序删除的时候经常使用命令RM,他的默认值为rm -f。
另外还有CFLAGS等默认值是调用编译器时的选项默认配置,例如修改后的Makefile生成main.o时,没有指定编译选项,make程序自动调用了文件定义的CFLAGS现象-Iadd -Isub -O2来增加头文件的搜索路径,在所有的目标文件中都采用此设置,所以之前的Makefile可以采用如下形式:
OBJS = add/add.o sub/sub.o main.o
CFLAGS = -Isub -Iadd -O2 #O2优化
TARGET = out
RM = rm -f
$(TARGET):$(OBJS)
$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)
clean:
-$(RM) $(TARGET) $(OBJS)
2)makefile的第三种写法(系统自动变量)
Makefile中除了用户自定义变量和预定义变量外,还有一类自动变量。Makefile中的编译语句中经常会出现目标文件和依赖文件,自动变量代表这些目标文件和依赖文件,如下图所示为一些常见的自动变量:
所以之前的Makefile可以写成如下所示:
OBJS = add/add.o sub/sub.o main.o
CFLAGS = -Isub -Iadd -O2 #O2优化
TARGET = out
$(TARGET):$(OBJS)
$(CC) $@ -o $^$(CFLAGS)
$(OBJS):%.O:%.c #将OBJS中所有扩展名为.o的文件替换为扩展名为.c的文件
$(CC) $< -c $(CFLAGS) -o $@ #编译生成目标文件
clean:
-$(RM) $(TARGET) $(OBJS)
4)makefile的第四种写法(自动推到规则)
使用命令make编译扩展名为c的C语言文件的时候,源文件的编译规则不用明确地给出,这是因为make进行编译的时候会使用一个默认的编译规则,按照默认规则完成对.c文件的编译,生成对应的.o文件。它执行命令cc-c来编译c源文件,在makefile中只需要给出需要重建的目标文件名(一个.o文件),make会自动为这个.o文件寻找合适的依赖文件(对应的c文件),并且使用默认的命令来构建这个目标文件。
对于上边的例子,默认规则是使用命令cc-c main.c-o main.o来创建文件main.o。对一个目标文件是“文件名.o”,倚赖文件是“文件名”的规则,可以省略其编译规则的命令行,由 make 命令决定如何使用编译命令和选项。此默认规则称为 make 的隐含规则。
这样在书写时,就可以省略掉描述c文件和o依赖关系的规则,而只需要给出那些特定的规则描述(目标所需要的.h文件),因此上面的例子可以使用更加简单的方式书写, Makele 文件的内容如下:
CFLAGS = -Isub -Iadd -O2
VPATH = add:sub #make命令自动搜索路径
OBJS = add.o sub.o main.o
TARGET = out
$(TARGET):$(OBJS)
$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)
clean:
-$(RM) $(TARGET)
-$(RM) $(OBJS)