资讯动态

Linux基础开发工具(二)程序编译与动静态库篇

发布时间:2026/10/15 4:07:11 来源:尧图企业网站定制
一、程序编译四阶段1. 预处理Preprocessing命令gcc -E test.c -o test.i输出文件test.i预处理后的 C 源码文本文件做什么只做文本替换不做语法检查#include头文件展开把#include stdio.h里面整个头文件内容直接复制粘贴到源码里。宏定义替换#define所有宏名字全部替换成对应文本。条件编译处理#ifdef / #ifndef / #endif根据条件删掉不参与编译的代码。删除注释所有//单行注释、/* */块注释全部清除。添加行号标记方便后续编译器报错定位。特点本质纯文本操作输出依然是 C 语言代码只是被展开修改过。不会检查语法错误。示例源码片段#define MAX 100 int a MAX;预处理后变成int a 100;2. 编译Compilation命令gcc -S test.i -o test.s输入.i预处理文件输出test.s汇编代码文件文本做什么C 语言 → 汇编语言这一步是真正的编译包含词法分析把代码拆成标记关键字、变量名、运算符、数字语法分析检查 C 语法是否合法语法错误在这里报错语义分析检查类型匹配、变量声明等语义问题代码优化对中间代码做简单优化生成汇编指令平台相关x86 /arm 汇编产物.s是可读的汇编文本是 CPU 指令的助记符。 不同 CPU 架构生成的汇编代码不一样。3. 汇编Assembly命令gcc -c test.s -o test.o输入.s汇编文本输出test.o目标文件ELF 二进制文件Linux做什么汇编代码 → 机器码读取汇编指令翻译成CPU 可以识别的二进制机器指令生成目标文件.oELF 格式Windows 下是.obj重点.o目标文件不能直接运行 原因代码里很多符号函数名、全局变量地址是未确定的只是占位符。 里面分成多个段.text代码段存放机器指令.data数据段全局初始化变量.bss未初始化全局变量查看目标文件符号命令nm test.o4. 链接Linking命令gcc test.o -o test输入一个或多个.o目标文件 库文件.a静态库 /.so动态库 输出最终可执行文件Linux ELF 可执行程序做什么合并多个目标文件 符号地址重定位 库引用解析链接分为两步符号解析找到每个符号函数、变量的定义重定位修正所有占位的地址把符号替换成真实内存地址链接分两种和前面讲的库对应静态链接把.a静态库里面用到的.o完整拷贝进可执行文件动态链接只记录.so动态库依赖运行时才加载库代码链接阶段常见报错undefined reference未定义引用就是找不到函数实现。执行程序./test汇总对比表可直接粘贴实验报告表格阶段命令输入文件输出文件核心工作预处理gcc -E.c源码.i预处理源码头文件展开、宏替换、删注释、条件编译处理纯文本操作编译gcc -S.i.s汇编文本词法 / 语法 / 语义分析C 代码转为汇编指令汇编gcc -c.s.o目标二进制汇编翻译为机器码生成 ELF 目标文件地址未填充链接gcc.o 库可执行文件合并目标文件符号解析、地址重定位链接库二、什么是库库Library是预先编译好的目标代码.o文件集合里面封装了已经写好的函数、变量我们写程序时不需要重复实现直接调用库里的函数即可。 在 Linux 下库分为两大类静态库Static Library.a后缀动态库 / 共享库Shared Library.so后缀目标文件.o源文件.c只编译不链接得到的二进制代码不能直接运行。三、静态库.a1. 原理静态库本质就是多个 .o 目标文件打包压缩在一起的归档文件。链接阶段链接器ld会把程序用到的库中的代码完整复制拷贝到最终可执行文件里面。程序编译链接完成之后静态库文件本身就不再依赖可以删除。2. 静态库命名规范libxxx.alib固定前缀xxx库名自定义.a静态库后缀示例libm.a数学静态库、libtest.a自己写的静态库3. 静态库制作步骤将.c源文件编译为目标文件.o只编译不链接gcc -c add.c sub.c -o add.o sub.o使用ar归档工具打包成静态库ar rcs libmymath.a add.o sub.or插入文件存在则覆盖c创建库文件s生成索引加快链接速度4. 使用静态库编译程序gcc main.c -o main -L. -lmymath-L.指定库搜索路径.代表当前目录-lmymath链接 libmymath.al后面直接写库名省略lib和.a5. 静态库优缺点优点运行时不需要依赖外部库文件可执行文件自带全部代码移植方便运行速度略快运行时不需要加载库。缺点空间浪费多个程序使用同一个静态库每个程序都会拷贝一份库代码磁盘占用大更新麻烦库代码修改后所有使用该库的程序都要重新编译链接重新生成可执行文件。四、动态库共享库.soShared Object1. 原理链接阶段不会把库代码拷贝进可执行文件仅仅记录库的依赖信息。程序运行阶段操作系统动态链接器ld-linux.so才会把动态库加载到内存多个进程共享内存中同一份库代码。动态库是运行时才加载所以运行程序时系统必须能找到这个.so文件。2. 动态库命名规范libxxx.solib 前缀xxx 库名.so动态库后缀一般带版本号libxxx.so.1.2软链接libxxx.so - libxxx.so.1.23. 动态库制作步骤编译生成位置无关代码-fPICgcc -c -fPIC add.c sub.c -o add.o sub.o-fPIC生成位置无关代码代码段没有绝对地址才能被加载到内存任意地址支持多进程共享。打包生成动态库gcc -shared add.o sub.o -o libmymath.so-shared生成共享库4. 使用动态库编译程序gcc main.c -o main -L. -lmymath编译命令写法和静态库几乎一模一样gcc 优先寻找动态库链接重要问题编译成功但运行报错error while loading shared libraries原因运行时动态链接器找不到.so文件。 三种解决方法临时设置环境变量export LD_LIBRARY_PATH$LD_LIBRARY_PATH:.把 so 放到系统库目录/usr/lib修改/etc/ld.so.conf更新缓存ldconfig查看程序依赖哪些动态库命令ldd main优点节省内存内存里只保留一份库多个进程共享磁盘也只存一份库文件升级简单更新.so文件不需要重新编译应用程序程序下次运行自动加载新版库。缺点运行依赖动态库文件库丢失程序直接无法运行运行时多一步加载启动速度相比静态库略慢。五、静态库 vs 动态库 对比汇总表表格对比项静态库 .a动态库 .so链接时机编译链接阶段拷贝代码进可执行文件编译链接仅记录依赖程序运行时加载库文件本质多个.o 归档打包ELF 共享目标文件依赖关系生成程序后不再依赖.a 文件运行程序必须存在.so 文件内存使用每个进程都独立复制一份库代码内存开销大内存仅一份库多进程共享节省内存程序体积可执行文件大可执行文件更小更新维护库修改 → 所有程序全部重新编译替换 so 文件即可应用不用重编译编译关键参数ar rcs 打包gcc -fPIC -shared适用场景嵌入式、独立小工具不想依赖外部文件大型系统多个程序共用同一套库如系统 libc六、总结编译四阶段预处理gcc -E纯文本处理展开头文件、宏替换、删除注释不检查语法输出.i。编译gcc -S词法、语法、语义分析与代码优化C 转汇编输出.s汇编文本。汇编gcc -c汇编转二进制机器码生成.o目标文件不可直接运行。链接gcc合并目标文件、符号解析与地址重定位结合库生成可执行程序链接报错常为undefined reference。Linux 库分为静态库.a、动态库.so静态库.a多个.o打包归档。链接时把库代码复制进可执行文件程序运行不再依赖.a可执行文件体积大库更新后程序需要重新编译。用ar rcs制作。动态库.so编译链接只记录依赖程序运行时才加载多进程共享内存里同一份库。体积小更新只需替换.so缺点是运行时必须能找到库文件编译时加-fPIC -shared生成。可用ldd查看程序依赖的动态库。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑