编译的四步骤

从源文件到可执行文件:预处理,编译,汇编和链接

  • 预处理
    • gcc -E
    • hello.c --> hello.i
    • 宏和头文件展开;删除注释,空白行;保留#pragma(它是编译器指令)
  • 编译
    • gcc -S
    • hello.i --> hello.s
    • 检查语法规范(词法分析,语法分析);生成汇编代码;消耗时间和系统资源最多的步骤
  • 汇编
    • gcc -c
    • hello.s --> hello.o
    • 将汇编指令翻译成机器指令;一对一翻译,相对机器来说比较简单
  • 链接
    • no specific parameters
    • 和其它的目标文件进行链接;数据段,符号表合并;符号重定位,地址回填;生成最终可执行文件

      常用参数

      -I :指定头文件所在的目录
      -c :只进行预处理,编译和汇编,生成二进制文件
      -g :编译时加入 debug 信息
      -On:n 取 0 ~ 3,编译优化,值越大,优化的越多
      -l:指定库的库名
      -L :指定库的搜索路径
      -Wall:显示所有警告信息
      -D:向程序中动态注册宏定义,一般起到「开关」的作用

      静态库和动态库

      静态库是在程序运行前就已经加入到程序代码中,成为程序代码的一部分,程序整体体积可能会比较大;动态库是在程序运行时加载到执行程序代码中,可以被多个程序共享使用,运行时效率稍微低一点。
      动态库相对独立,便于维护和更新;静态库和程序绑在一起,方便移植,不会出现依赖问题。
      静态库一般以.a为后缀,动态库以.so为后缀。
      发布静态库时,要提供.a.h文件;

      静态库制作过程

  1. hello.c中的函数作为静态库来提供使用。 ```c

    include

    include “hello.h”

void hello() { printf(“hello world\n”); }

  1. 2. 编译生成`.o`文件
  2. ```bash
  3. gcc -c hello.c -o hello.o
  1. 使用ar工具来制作静态库

    1. ar -rsc libhello.a hello.o

    库名默认以lib作为前缀,以.a作为后缀;.o文件可以有很多个,将多个目标文件压成一个静态库。
    image.png

  2. 编译源文件,把静态库也加入编译 ```c

    include “hello.h”

int main() { hello(); return 0; }

  1. 库要写在源文件后面。该库进行静态编译,其它库(例如`printf()`默认动态编译)。
  2. ```bash
  3. gcc main.c libhello.a -o main
  4. gcc main.c -o main -lhello -L. # 如果./只有.a文件可以这么编译,否则默认链接动态库
  1. 运行

    1. $./main
    2. hello world

    静态编译生成的可执行文件一般体积都挺大。
    image.png

    动态库制作过程

  2. hello.c中的函数作为动态库来提供使用 ```c

    include

    include “hello.h”

void hello() { printf(“hello world\n”); }

  1. 2. 编译生成`.o`文件,**此时需生成与位置无关的代码,使用**`**-fPIC**`**参数,表示各个变量、函数的地址使用相对地址,而不是绝对地址**
  2. ```bash
  3. gcc -c hello.c -o add.o -fPIC
  1. 使用gcc -shared来制作动态链接库,.o文件可以有很多个

    1. gcc -shared hello.o -o libhello.so

    image.png

  2. 编译源文件,指定动态库的位置。

    1. gcc main.c -o main -lhello -L.

    image.png
    可以看到图中多了一个动态库的信息;可执行文件体积也更小。

  3. 配置动态链接器的库搜索路径;运行

动态链接库是运行时被加载,要保证在运行时动态链接器能找到这个库文件。上面截图显示libhello.so未被找到。

  • 将库文件移动至系统默认的搜索路径下(例如,/usr/lib
  • 设置库搜索的环境变量LD_LIBRARY_PATH;当前命令行设置,一次性,shell 关闭后失效;在.xxshrc配置文件中设置;建议使用绝对路径。

image.png

  • 修改/etc/ld.so.conf配置文件,将库绝对路径加入到文件中;执行ldconfig更新配置。

    GCC 内联函数

    声明内联函数有两种方式:

  • inline

  • __attribute __((always_inline))

第一种是建议编译器使用内联,实际是否内联由编译器来决定;第二种是强制编译器将函数当作内联函数。