多文件编译

目录

1 引言

作为一名通信工程师,通常的C/C++代码都是只有一个文件,比如Turbo编码器或者译码器,LDPC编码器或者译码器。但是,偶尔也会用到需要编译多个文件的时候。我对庞大的Visual Studio又比较畏惧(主要是我那256G的硬盘比较畏惧),倾向于使用GCC。所以本文记录使用 GCCmake 编译多个文件。

2 九个文件的工程

首先给出有九个文件的工程,这九个文件分别是 sum.c sum.h substract.c substract.h multiply.c multiply.h divide.c divide.h main.c 其内容分别是实现浮点加减乘除。为节省篇幅,仅列出 sum.hsum.c 。首先 sum.h ,这个文件只有一行,即 sum() 函数的声明。

float sum(float a, float b);

然后是 sum.c ,这个文件实现了 sum() 函数。

#include "sum.h"

float sum(float a, float b){
  return a + b;
}

主文件 main.c 是:

#include <stdio.h>
#include <math.h>
#include "sum.h"
#include "substract.h"
#include "multiply.h"
#include "divide.h"

int main(int argc, char *argv[])
{
  float a,b;
  a = 3;
  b = 4;
  printf("%f = %f + %f\n",sum(a,b),a,b);
  printf("%f = %f - %f\n",substract(a,b),a,b);
  printf("%f = %f * %f\n",multiply(a,b),a,b);
  printf("%f = %f / %f\n",divide(a,b),a,b);

  return 0;
}

编译这九个文件的常规做法是逐个编译,或者使用:

gcc main.c sum.c substract.c multiply.c divide.c -o main

但是这种做法的缺点是明显的:

  1. 随着文件的增多,编译工作将是一个工作量很大的无营养工作。
  2. 对于没有修改过的文件也要重新编译,无疑将增加编译时间

3 make

我的make是跟着陈皓学的(陈皓是著名文档《跟我一起写Makefile》的作者。对于每个严肃的make学习者,这个文档都是极佳的入门资料。)

这个工程的makefile可以写为:

main: main.o sum.o substract.o multiply.o divide.o
  gcc main.o sum.o substract.o multiply.o divide.o -o main
main.o: main.c
  gcc -c main.c
sum.o: sum.c
  gcc -c sum.c
substract.o:substract.c
  gcc -c substract.c
multiply.o: multiply.c
  gcc -c multiply.c
divide.o: divide.c
  gcc -c divide.c

make有很多特殊的变量,使用这些变量可以编写更加简洁的makefile。但是这些变量的使用,会使得不太熟悉makefile的人读起来费劲。对于只有九个文件的小工程,我倾向于使用这种最原始直观的makefile。即使这种最原始的makefile也极大的降低了编译工作量。

4 静态链接库

在实际工程中,我们希望把编好的程序打包变成库,供以后使用。windows和linux下都有两种打包后的程序:静态库和动态库。关于静态库和动态库,本文不做过多介绍。 这篇 博文 对C/C++ 跨平台交叉编译,静态库和动态库编译有比较详细的介绍。更详细的介绍见《程序员的自我修养:链接、装载与库》。

接下来,我演示如何在windows上使用gcc编译并使用静态库文件。静态库文件的编译非常简单,接着上面的工程:

gcc -c sum.c

就会生成静态库文件 sum.o 。依次类推,生成 substract.o multiply.o divide.o ,然后使用 ar 把这些静态库打包:

ar rcs libmymath.a sum.o substract.o multiply.o divide.o

编译 main.c 的时候就可以链接我们刚才生成的静态库,编译命令是:

gcc -o main main.c -L. -lmymath

有两点需要注意:

  1. -L. 告诉GCC除了默认目录外当前目录也是搜索静态库的目录。
  2. -lmymath 告诉GCC链接的库的名字是 mymath.a .

5 动态链接库

较少人使用gcc在windows下构建动态链接库,其过程并不复杂。但是,鉴于动态链接库的优点,基于本文的工程,演示一下如何构建使用gcc构建动态链接库。

gcc -c main.c
gcc -c -DBUILD_DLL sum.c
gcc -shared -o sum.dll -Wl,--out-implib,libsum.a sum.o
gcc -c -DBUILD_DLL substract.c
gcc -shared -o substract.dll -Wl,--out-implib,libsubstract.a substract.o
gcc -c -DBUILD_DLL multiply.c
gcc -shared -o multiply.dll -Wl,--out-implib,libmultiply.a multiply.o
gcc -c -DBUILD_DLL divide.c
gcc -shared -o divide.dll -Wl,--out-implib,libdivide.a divide.o
gcc -o maindll.exe main.o -L. -lsum -lsubstract -lmultiply -ldivide

终于生成了四个动态链接库分别保存了 sumsubstractmultiplydivide 的实现。关于在windows下使用gcc编译动态链接库的更复杂的例子请见 MinGW的帮助文档。