1.2 编译基础知识

一、编译简介

项目编译都经历了两个阶段:编译链接

  • 无论项目多么庞大, 编译都是单文件编译,且编译时不用管函数实现,只需知道函数定义即可,所以编译要找到对应的头文件。(编译时用00占位符,表示函数地址)
  • 链接才支持多文件输入, 这阶段会修正函数地址,将上面的00修改为函数的真正地址。静态库链接不是将静态库的内容全部复制,只有用到才会复制。

库类型:

  • 静态库是一个归档文件,即打包文件。可以对其进行解压,解压后的.o文件再和自己功能.o文件,重新打包成新的静态库。静态库里面的函数地址还是00,所以打包静态库不涉及链接操作(即编译+打包)
  • 动态库:函数地址不是00,但也不是真正的函数地址,是符号表的地址,动态库是通过符号表跳转到函数真正地址。(Linux的动态库是将函数符号信息和函数实现放在一个文件,而Windows的MSVC是分开两个文件dll和import lib)

二、头文件和依赖库搜索路径

2.1 头文件

(1)默认搜索路径

编译器(如gcc、clang)会按照如下顺序搜索头文件:

  1. 当前目录:对与使用双引号#include "header.h"的情况,编译器会首先在源文件所在目录查找。
  2. 标准系统路径:对于使用尖括号#include <header.h>的情况,编译器会查找系统默认路径,通常包括:
    • /usr/include
    • /usr/local/include
    • 编译器内置路径(如/usr/lib/gcc/x86_64-linux-gnu/<version>/include

(2)自定义搜索路径

通过编译器选项 指定头文件搜索路径:

  • I<路径>:指定头文件搜索的目录,优先级高于系统默认路径
    • 示例:gcc -I./include main.c
  • -isystem <路径>:指定系统头文件路径,优先级低于 -I 但高于标准路径
  • 环境变量:某些编译器会参考环境变量 CPATHC_INCLUDE_PATH(C 程序)或 CPLUS_INCLUDE_PATH(C++ 程序)来查找头文件。
    • 示例:export C_INCLUDE_PATH=/path/to/include

2.2 库文件

2.2.1 链接阶段

(1)默认搜索路径

链接器通常会搜索以下标准路径:

  • /lib
  • /usr/lib
  • /usr/local/lib
  • 编译器特定的库路径(如 /usr/lib/gcc/x86_64-linux-gnu/<version>
(2)自定义搜索路径

通过编译器选项 指定库搜索路径:

  • -L<路径>:在链接时指定库文件的路径,优先级高于系统默认路径
    • 示例:gcc main.c -L./libs -lmylib
  • 环境变量LIBRARY_PATH:链接器会参考 LIBRARY_PATH 环境变量指定的路径。
    • 示例:export LIBRARY_PATH=/path/to/libs
  • 使用 -l<库名> 指定要链接的库,链接器会自动在搜索路径中查找 lib<库名>.alib<库名>.so
    • 示例:-lm表示链接 libm.solibm.a(数学库)。

2.2.2 运行阶段(动态链接)

  • 程序中指定的 RPATH:编译时通过 -rpath--rpath选项嵌入到可执行文件中的路径。
    • 示例:gcc -Wl,-rpath,/path/to/libs main.c
  • LD_LIBRARY_PATH 环境变量:运行时指定的动态库路径,优先级高于系统默认路径。
    • 示例:export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH
  • /etc/ld.so.cache:由ldconfig工具生成,缓存了系统默认路径中的库信息。
    • 通常包括 /lib/usr/lib/usr/local/lib
    • /etc/ld.so.conf/etc/ld.so.conf.d/*.conf 中配置的路径加入缓存
    • 查看当前动态库缓存:ldconfig -p
    • 更新动态库缓存:sudo ldconfig
  • 系统默认路径:如/lib/usr/lib

2.3 常见问题与解决方法

  • 头文件未找到
    • 检查头文件路径是否正确,使用 -I 指定路径。
    • 确保头文件与源代码的 #include 语法匹配(双引号或尖括号)。
  • 库文件未找到(链接时)
    • 使用 -L 指定库路径,检查库文件是否存在。
    • 确保 -l 指定的库名正确(去掉 lib 前缀和 .a/.so 后缀)。
  • 动态库未找到(运行时)
    • 检查 LD_LIBRARY_PATHRPATH 设置。
    • 确保动态库已安装并在 ldconfig 缓存中。
    • 使用 ldd 检查依赖是否正确解析。

三、如何指定静态链接和动态链接

3.1 静态链接

静态链接是指在编译时将库的代码直接嵌入到生成的可执行文件中,生成的可执行文件包含了所有需要的代码,运行时无需依赖外部库文件

注意:不是将库的所有代码嵌入,而是程序用到的函数代码

常见方式

  • 使用-static选项,强制使用静态库链接
    • 示例:gcc main.c -o myprogram -static # 会链接静态运行时库
  • 用静态库全名
    • 示例:gcc main.c -L/path/to/lib -l:libexample.a -o myprogram

3.2 动态链接

动态链接是指在编译时仅在可执行文件中记录对动态库(.so 文件)的引用,运行时动态加载这些库。动态链接是大多数编译器的默认行为

常见方式

  • 默认行为: GCC 和 Clang 默认使用动态链接,因此通常不需要额外指定。
  • 显式指定动态库:使用 -l 选项指定动态库名称(去掉 lib 前缀和 .so 后缀)
    • 示例:gcc main.c -L/path/to/lib -lexample -o myprogram

3.3 混合链接

在某些情况下,可以混合使用静态和动态链接。例如,强制链接某些库为静态,而其他库保持动态。可以使用 -Wl,-Bstatic-Wl,-Bdynamic 选项来控制:

  • -Wl,-Bstatic:接下来的 -l 指定的库将以静态方式链接。
  • -Wl,-Bdynamic:接下来的 -l 指定的库将以动态方式链接。

示例:

gcc -o program source.o -L/usr/local/lib -Wl,-Bstatic -lexample -Wl,-Bdynamic -lanother
  • libexample.a 将被静态链接。
  • libanother.so 将被动态链接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值