《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会用

 

  

 

posted @ 2019-10-29 11:14  一个不知道干嘛的小萌新  阅读(557)  评论(0编辑  收藏  举报