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.ofile2.ofile3.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.dlllibcmt.lib

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

四、静态库/动态库

1.静态库

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

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

2.动态库

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

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

相关推荐
捧月华如5 小时前
Linux 系统性能压测工具全景指南(含工程实战)
linux·运维·服务器
YMWM_5 小时前
export MPLBACKEND=Agg命令使用
linux·python
想唱rap5 小时前
线程的同步与互斥
linux·运维·服务器·数据库·mysql
格林威6 小时前
SSD 写入速度测试命令(Linux)(基于工业相机高速存储)
linux·运维·开发语言·人工智能·数码相机·计算机视觉·工业相机
勇闯逆流河6 小时前
【LInux】linux控制(进程替换,自主shell的实现详解)
linux·运维·服务器
IMPYLH6 小时前
Linux 的 ls 命令
linux·运维·服务器·bash
笨笨饿7 小时前
33_顺序表(待完善)
linux·服务器·c语言·嵌入式硬件·算法·学习方法
wwj888wwj7 小时前
Ansible基础(复习1)
linux·运维·ansible
yj_xqj7 小时前
Linux network启动报错 && nmcli 的使用
linux·运维·服务器