C/C++ 程序编译过程、静态/动态链接、静态/动态库

一、编译的四个阶段

1.预处理(进行宏替换)

预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
预处理指令是以#号开头的代码行。

bash 复制代码
gcc -E code.c -o code.i

2.编译(生成汇编)

在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,
在检查无误后,gcc 把代码翻译成汇编语言。

bash 复制代码
gcc --S hello.i --o hello.s

3.汇编(生成机器可识别代码)

将汇编代码转换为二进制机器码。

bash 复制代码
gcc --c hello.s --o hello.o

4.连接(生成可执行文件或库文件)

链接器(Linker)将多个目标文件和库文件合并成一个完整的可执行文件。它会解决所有未定义的符号引用,将它们指向正确的地址。链接器还会将程序使用的标准库函数(如 printf)合并进来。

bash 复制代码
gcc hello.o --o hello

二、实际项目应用

在真实的软件开发中,一个项目通常由多个源文件(.c文件)组成。我们通常会分别将每个源文件编译为目标文件,而不是一次性编译整个项目。这可以通过以下方式实现:

bash 复制代码
# 分别编译每个源文件为目标文件
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
gcc -c file3.c -o file3.o

或:

bash 复制代码
gcc -c file1.c
gcc -c file2.c
gcc -c file3.c

这会产生对应的 file1.o、file2.o、file3.o文件。这种分离编译的方式有以下几个优点:

  1. 当只修改其中一个源文件时,只需重新编译该文件,而不用重新编译整个项目,提高了编译效率。

  2. 便于团队协作,不同开发者可以并行编译各自负责的模块。

  3. 便于将代码组织成库文件。

最后一起链接:

bash 复制代码
# 将所有目标文件链接成一个可执行文件
gcc file1.o file2.o file3.o -o myprogram

当只修改其中一个源文件时,只需重新编译该文件,而不用重新编译整个项目,提高了编译效率。

操作系统是怎么做到的??

看时间戳

Linux 里每个文件都有 3 个时间,用 stat 文件名 可以看到:

atime :访问时间(cat、vim 打开看一下,就变)

mtime :修改时间(内容改了才变)

ctime :状态改变时间(权限、改名等)

操作系统只关心一个:mtime(修改时间)

只查看文件 → mtime 不变,make 就认为文件没动 → 不会重新编译

修改文件、保存文件 → mtime 变 → 会重新编译

判断流程:

比较目标文件和依赖文件的 mtime

如果依赖文件比目标文件更新(mtime 更大)→ 执行命令

如果目标文件不存在 → 执行命令

否则 → 跳过("目标已是最新的")


三、静态链接 vs 动态链接

在实际开发中,多个源文件之间存在依赖关系,需要将它们产生的目标文件进行链接。链接方式有两种:

1.静态链接

在编译时,将库文件的代码全部复制到可执行文件中。

优点:

可执行文件独立,不依赖外部库文件。

启动速度快(代码在进程地址空间内)

缺点:

浪费空间(多个程序包含相同库的副本)

更新困难(库更新需重新编译整个程序)

可执行文件体积较大

2.动态链接

在程序运行时才加载库文件。程序启动稍慢,但内存占用少。

例如:

我们的C程序中调用的 printf()函数,实现在哪里?

C标准只定义了函数声明(在 stdio.h中),函数实现位于系统的C标准库中:

Linux: libc.so.6(动态库)或 libc.a(静态库)

Windows: msvcrt.dll或 libcmt.lib

核心:让程序找到库中方法的地址!

四、静态库/动态库

1.静态库

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,其后缀名⼀般为".a"

静态库本身是一个文件(例如 libmymath.a),它本质上是一个或多个目标文件(.o文件)的打包集合 。创建它的目的是为了代码复用。

2.动态库

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由
运行时链接文件加载库,这样可以节省系统的开销。动态库⼀般后缀名为".so"

gcc默认生成的二进制程序,是动态链接的。gcc 在编译时默认使用动态库。
可以使用 -static 强制全部静态链接。

相关推荐
Apipi*2 小时前
30天速通Linux 第六章信号及信号处理
linux·运维·信号处理
天启HTTP3 小时前
爬虫防封核心原理:IP轮换机制与风控规避逻辑
linux·服务器·网络·爬虫·tcp/ip
广州浮点FLOATLIC6 小时前
许可证服务器迁移后软件打不开:研发 IT 怎样定位连接问题
linux·服务器·数据库
BLUcoding6 小时前
接口服务公网超时排查记录:SecureRandom 阻塞问题分析与解决
java·linux·springboot·aes·securerandom
AIgorithmGEEK6 小时前
[Linux]从手写报头到内核套路:序列化、反序列化与自定义协议全链路
linux·运维·服务器·网络·序列化·反序列化
Nil2086 小时前
leetcode 139单词拆分
linux·运维·服务器
Starry-sky(jing)6 小时前
BUG: unable to handle kernel paging request 完整排查:dmesg 四要素与三路定罪
linux·运维·服务器·内核·排障
mounter6256 小时前
从硬件互连到操作系统变革:CXL 技术演进与 Linux 内核工程挑战
linux·运维·服务器
xh didida8 小时前
Linux -- 基础IO
linux·服务器·开发语言·c++
zly35009 小时前
centos7 mount设备挂载 /dev/sda1/硬盘挂载
linux·运维·服务器