本文介绍GCC的编译链接与执行,下面是编译链接常用到的参数及其描述:
参数 | 描述 |
---|---|
-c | 编译、汇编指定的源文件(也就是编译源文件),但是不进行链接 |
-o | 用来指定输出文件 |
-L | 为 gcc 增加一个搜索链接库的目录 |
-l | 用来指定程序要链接的库 |
0.静态连接库和动态链接库的区别
静态库和动态库最本质的区别就是:该库是否被编译进目标(程序)内部。
0.1静态(函数)库
一般扩展名为.a
或.lib
,这类的函数库通常扩展名为libxxx.a
或xxx.lib
。
这类库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。
0.2动态函数库
动态函数库的扩展名一般为.so
或.dll
,这类函数库通常名为libxxx.so
或xxx.dll
。
与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。
1.主程序的编译、链接与执行
在代码目录下编写main.c
文件,功能是输出字符串Hello Cacu!
,内容如下:
#include <stdio.h>
int main(void)
{
printf("Hello Cacu!\n");
return 0;
}
使用gcc -c main.c
命令进行编译,发现目录想生成了一个main.o
文件,如下:
通过 file
命令查看 main.o
的文件格式:
这说明 main.o
实际上是一个relocatable object
文件。通过以下命令为 main.o
文件赋予可执行的权限:
chmod 777 main.o
输入以下命令尝试执行 main.o
文件:
./main.o
Terminal 输出可执行文件格式错误,如图所示:
说明 relocatable object
文件是不可执行的。
接下来通过 GCC 对main.o
文件进行链接操作,从而生成一个可执行的程序 main
。
在 Terminal 中输入以下命令将 main.o
链接为 main
文件:
gcc -o main main.o
可以发现当前目录新增了一个名为 main
的文件。
通过 file
命令查看 main
的文件格式:
file main
输出结果如图所示:
说明 main
文件是一个可执行的文件,于是通过以下命令来执行 main
文件:
./main
输出结果如图所示:
说明程序得到了正确的执行。
2.静态链接
编写 add_minus.h
文件,在文件中对函数 add()
和 minus()
进行声明。编写 add_minus.c
文件,实现函数 add()
和 minus()
。
#ifndef __ADD_MINUS_H__
#define __ADD_MINUS_H__
int add(int a, int b);
int minus(int a, int b);
#endif /*__ADD_MINUS_H__*/
#include "add_minus.h"
int add(int a, int b)
{
return a+b;
}
int minus(int a, int b)
{
return a-b;
}
对 add_minus.c
文件进行编译,生成 add_minus.o
文件。
gcc -c add_minus.c
修改 main.c
文件,为其增加加减法运算并编译这个文件。
#include <stdio.h>
#include "add_minus.h"
int main(void)
{
int rst;
printf("Hello Cacu!\n");
rst = add(3,2);
printf("3 + 2 = %d\n",rst);
rst = minus(3,2);
printf("3 - 2 = %d\n",rst);
return 0;
}
通过以下命令对 main.c
文件进行编译和链接:
gcc -c main.c
gcc -o main main.o
链接生成的 main.o
文件时,发现有错误出现,错误内容如图所示:
原因在于链接过程中找不到 add
和 minus
这两个 symbol。现将 main.o
和 add_minus.o
链接成可执行文件并执行测试。
gcc -o main main.o add_minus.o
执行新生成的可执行文件 main
。
./main
重新编译 add_minus.c
生成 add_minus.o
文件。
gcc -c add_minus.c
通过 ar 命令将 add_minus.o
打包到静态库中。
ar rc libadd_minus.a add_minus.o
可以发现在当前目录下,生成了一个名为 libadd_minus.a
的静态库文件
用 file
命令查看 libadd_minus.a
的文件格式。
file libadd_minus.a
Terminal 输出结果如图所示:
实际上 libxxx.a
格式的文件可以简单的看成指定的以 .o
结尾的文件集合。
链接 main.o
和静态库文件:
gcc -o main2 main.o -L./ -ladd_minus
-
-L./
:表明库文件位置在当前文件夹。 -
-ladd_minus
:表示链接libadd_minus.a
文件,使用-l
参数时,前缀lib
和后缀.a
是需要省略的。
执行 main2
:
./main2
Terminal 输出结果如图所示,说明程序的到了正确的执行。
3.动态链接
编写 multi_div.h
文件,并在其中对函数 multi()
和 div()
进行声明。编写 multi_div.c
文件,实现函数 multi()
和 div()
。
#ifndef __MULTI_DIV_H__
#define __MULTI_DIV_H__
int multi(int a, int b);
int div(int a, int b);
#endif /*__MULTI_DIV_H__*/
#include "multi_div.h"
int multi(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a/b;
}
通过以下命令将 multi_div.c
文件编译成动态链接库。
gcc multi_div.c -fPIC -shared -o libmulti_div.so
-fPIC
选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码。
命令执行结束后,在当前目录下会生成一个名为libmulti_div.so
的文件。
通过 file
命令来查看 libmulti_div.so
的文件格式。
file libmulti_div.so
Terminal 输出结果如图所示:
由此可知 libmulti_div.so
是一个 shared object
文件。
编写main.c文件如下:
#include <stdio.h>
/*
#include "add_minus.h"
*/
#include "multi_div.h"
int main(void)
{
int rst;
printf("Hello Cacu!\n");
/*
rst = add(3,2);
printf("3 + 2 = %d\n",rst);
rst = minus(3,2);
printf("3 - 2 = %d\n",rst);
*/
rst = multi(3,2);
printf("3 * 2 = %d\n",rst);
rst = div(6,2);
printf("6 / 2 = %d\n",rst);
return 0;
}
编译 main.c
生成 main.o
:
gcc -c main.c
链接 main.o
与动态链接库文件:
gcc -o main3 main.o -L./ -lmulti_div
执行生成的 main3
文件:
./main3
输出结果出现错误,如图所示:
出现错误的原因是我们生成的动态库 libmulti_div.so
并不在库文件搜索路径中。
解决办法:将 libmulti_div.so
拷贝到/lib/
或/usr/lib/
文件夹下。
sudo cp libmulti_div.so /usr/lib
现在在 Terminal 中执行下面的命令:
./main3
说明程序得到了正确的执行。
4.混合使用静态链接与动态链接
编写新的main.c
程序如下,仍然调用前面章节使用的add_minus.h
、add_minus.c
、multi_div.h
和multi_div.c
文件。
#include <stdio.h>
#include "add_minus.h"
#include "multi_div.h"
int main(void)
{
int rst;
printf("Hello Cacu!\n");
rst = add(3,2);
printf("3 + 2 = %d\n",rst);
rst = minus(3,2);
printf("3 - 2 = %d\n",rst);
rst = multi(3,2);
printf("3 * 2 = %d\n",rst);
rst = div(6,2);
printf("6 / 2 = %d\n",rst);
return 0;
}
编译 main.c
生成 main.o
。
gcc -c main.c
测试执行混用静态链接和动态链接的方式。
gcc -o main4 main.o -L./ -ladd_minus -lmulti_div
执行下面的命令:
./main4
输出结果如图所示: