《GCC编译器的使用以及静态库和动态库的制作与使用》
1.源码
main.c #include <stdio.h> #include "sub.h" int main(int argc, char *argv[]) { int i; printf("Main fun!\n"); sub_fun(); return 0; } sub.c void sub_fun(void) { printf("Sub fun!\n"); } sub.h void sub_fun(void);
注意:头文件stdio.h,如果用gcc去编译程序,一般stdio.h在/usr/include中。如果是arm-linux-gcc,那么stdio.h就位于这个编译器目录里面的usr/include。也可以通过-I去指定一个路径里面的头文件,或者使用"stdio.h",表示在当前目录中的stdio头文件。
最终的可执行程序需要通过main.c和sub.c一起编译才可以。
gcc -o test main.c sub.c
最后会生成一个名为test的可执行文件。
警告:
这边提示的是隐式函数声明,只需要在sub.c中定义一下#include <stdio.h>。关于隐式函数更加详细说明点击这里。
2.编译
在日常交流中通常使用“编译”统称这4个步骤,实际上分为四个步骤:一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等4步才能变成可执行文件。
预处理(preprocessing):C/C++源文件中,以“#”开头的命令被称为预处理命令,如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个“.i”文件中等待进一步处理。
编译(compilation):编译就是把C/C++代码(比如上述的“.i”文件)“翻译”成汇编代码,所用到的工具为cc1(它的名字就是cc1,x86有自己的cc1命令,ARM板也有自己的cc1命令),生成.s文件。
汇编(assembly):汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现为ELF目标文件(OBJ文件),用到的工具为as。x86有自己的as命令,ARM版也有自己的as命令,也可能是xxxx-as(比如arm-linux-as),最后生成.o文件。
反汇编:是指将机器代码转换为汇编代码,这在调试程序时常常用到。
链接(linking):链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的可执行文件,用到的工具为ld或collect2,最后生成可执行程。
注意:指定头文件目录的是-I(大写的i),而指定库文件的是-l(是小写的L)。
3.怎么编译多个文件
3.1 一起编译、链接:
gcc -o test main.c sub.c(建议源文件少的情况下使用)
3.2 分开编译,统一链接:
gcc -c -o main.o main.c (建议源文件多的情况下使用) gcc -c -o sub.o sub.c gcc -o test main.o sub.o
注意:用3.1的方法,修改一个.c文件,那么全部的.c文件都将会被重新编译。对于源文件少的没有影响,但是如果源文件多,效率很低。
3.2的方法,修改一个.c文件,只编译修改的那个。
4.制作、使用动态库
4.1制作、编译
gcc -c -o main.o main.c gcc -c -o sub.o sub.c gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o生成动态库) gcc -o test main.o -lsub -L /libsub.so/所在目录/
其中lsub就是libsub.so通过-l指定具体的库,通过-L指定库所在的目录。如果不指定库所在的目录,编译器默认去lib下面查找。
4.2在开发板运行
1.因为使用的是动态库,必须将动态库放在开发板的/lib或者/usr/lib下,因为在执行应用程序的时候,默认去这两个目录中查找。
2.如果不想把libusb.so放到/lib,也可以放在某个目录比如/a,然后如下执行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/a
./test
3.添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新。
5.制作、使用静态库
gcc -c -o main.o main.c gcc -c -o sub.o sub.c ar crs libsub.a sub.o sub2.o sub3.o(可以使用多个.o生成静态库) gcc -o test main.o libsub.a (如果.a不在当前目录下,需要指定它的绝对或相对路径)
运行:
不需要把静态库libsub.a放到板子上。但是可执行程序的大小会大很多。
6.静态库和动态库的优缺点
6.1静态库
编译(链接)时把静态库中相关代码复制到可执行文件中(占用更多磁盘和内存空间)。
程序中已包含代码,运行时不再需要静态库(运行时无需加载库,运行速度更快)。
静态库升级后,程序需要重新编译链接(不利于后期的升级维护)。
6.2动态库
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码(尺寸小)。
多个程序可共享同一个库。
程序运行时需要加载库(运行速度受影响)。
库升级方便,无需重新编译程序。
7.gcc中一些比较好用的选项
gcc -E main.c // 查看预处理结果,比如头文件是是去哪里查找的 gcc -E -dM main.c > 1.txt // 把所有的宏展开,存在1.txt里,可以清楚的看到宏最后定义了什么 gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依赖文件abc.dep,后面Makefile会用