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 强制全部静态链接。

相关推荐
charlie1145141914 分钟前
嵌入式Linux驱动开发(7) 从虚拟设备到真实硬件 —— LED驱动硬件基础
linux·开发语言·驱动开发·内核·c
Mortalbreeze20 分钟前
软件包管理器yum和编辑器vim详解 —— 附带vim配置链接
linux·服务器
李日灐1 小时前
< 7 > Linux 开发工具:git 版本控制器 和 cgdb/gdb 调试器
linux·运维·服务器·开发语言·git·调试器·gdb/cgdb
青木9601 小时前
前后端开发调试运行技巧
linux·服务器·前端·后端·npm·uv
c++之路1 小时前
C++ 模板
linux·开发语言·c++
云动课堂1 小时前
【运维实战】MySQL 8.0 数据库 · 一键自动化部署方案 (适配银河麒麟 V10 / 龙蜥 8 / Rocky Linux 8 / CentOS 8)
linux·运维·数据库
cui_ruicheng1 小时前
Linux进程间通信(一):管道与IPC基础
linux·运维·服务器
Lumos_7771 小时前
Linux -- 互斥锁
linux
一叶龙洲2 小时前
Ubuntu开机无法用向日葵远程控制
linux·运维·ubuntu
计算机安禾2 小时前
【Linux从入门到镜头】第29篇:文本处理三剑客(下)——awk 数据处理神器
linux·运维·服务器