【C语言编译】编译原理和详细过程

文章目录

  • 1. C 语言编译原理和详细过程
    • 1.1 预处理阶段
    • 1.2 编译阶段
    • 1.3 汇编阶段
    • 1.4 链接阶段
  • 2. 疑问点解析
    • 2.1 三地址码是什么?有什么作用
    • 2.2 符号表是什么?有何作用
    • 2.3 重定位的含义与作用
    • 2.3 符号表和重定位在整个编译过程中的作用
    • 2.4 动态链接库.so和静态链接库.a
    • 2.5 不要混淆.o文件和.so文件
  • 3. 知识补充

1. C 语言编译原理和详细过程

编译是将源代码转换为计算机可执行的二进制文件的过程,整个过程主要分为四个阶段: 预处理; 编译; 汇编; 链接

1.1 预处理阶段

工具:预处理器
输入:.c 源文件
输出:.i 预处理后的文件

预处理阶段做的主要工作:

  • 宏展开:将所有#define定义的宏进行文本替换
  • 头文件包含:递归展开#include指令,将头文件内容插入源代码
  • 条件编译:处理条件编译指令(如#ifdef等命令),根据条件保留或删除代码块(如调试代码)
  • 删除注释

1.2 编译阶段

工具:编译器
输入:.i文件
输出:.s汇编代码文件

编译阶段做的主要工作:

  • 词法分析:将代码拆分为token(如标识符,关键字,运算符)
  • 语法分析:构建抽象语法树(AST),检查语法是否符合C标准(如检查括号是否匹配,语句是否合法)
  • 语义分析:检查类型匹配,变量声明,作用域规则
  • 中间代码生成:生成与平台无关的中间表示(如三地址码)
  • 代码优化:对中间代码进行优化。如删除冗余代码、常量折叠等
  • 目标代码生成:将优化后的中间代码转换为目标平台的汇编代码

1.3 汇编阶段

工具:汇编器
输入:.s汇编文件
输出:.o目标文件,即二进制机器码
汇编阶段做的主要工作:

  • 指令转换:将汇编代码逐行转换为机器码
  • 生成目标文件:生成包含机器码、符号表、重定位信息的**.o文件**

1.4 链接阶段

工具:链接器
输入:多个.o目标文件+静态库.a文件
输出:可执行文件
链接阶段做的主要工作:

  • 符号解析:解决跨文件的函数或变量引用。如main.o中调用printf函数,则需找到该函数在libc.a中的定义
  • 重定位:合并所有目标文件的代码段、数据段,分配最终内存地址;修正符号表中的地址偏移量
  • 库文件处理
      - 静态链接:将静态库.a代码直接复制到可执行文件中
      - 动态链接:记录动态库.so的路径,运行时加载
  • 生成可执行文件:生成符合操作系统格式的可执行文件

注:可执行文件分为 代码段(.text)、数据段(.data)、未初始化数据段(.bss)

经过这一系列过程,c源代码最终变成了操作系统可直接执行的二进制文件 运行时由加载器将其读入内存并执行。

2. 疑问点解析

2.1 三地址码是什么?有什么作用

三地址码(Three-Address Code,TAC)是编译器中常用的一种中间表示(Intermediate Representation, IR)形式,它将复杂的表达式和语句拆解为一系列简单的指令,每条指令最多包含三个操作数。它的核心目标是简化代码优化和目标代码生成的过程,同时保持与机器无关的特性。
每条指令仅包含一个操作(如赋值,运算,跳转等),最多涉及三个操作数,两个输入一个输出。常见的通用格式如下:

result = operand1 op operand2

2.2 符号表是什么?有何作用

符号表是编译器/汇编器生成的一种数据结构,记录了程序中所有符号的信息:

  • 符号名称:如函数名、全局变量名称;
  • 符号类型:函数、变量、静态/全局作用域等;
  • 符号地址:在目标文件中的相对地址或内存地址。

符号分类:

  • 全局符号:可被其他文件访问的符号,如extern变量、非static函数;
  • 局部符号:仅在本文件内可见的符号,如static函数或变量;
  • 外部符号:在本文件中使用但未定义的符号,如调用了其他文件中的函数。

在链接阶段,链接器通过符号表解析不同目标文件之间的符号引用,如函数调用、变量访问等;符号表包含了符号的地址和类型,支持调试器(如gdb)定位代码和变量(比如有时候调试的时候会导入符号表);动态链接库.so需要依赖符号表在运行时绑定函数地址。

2.3 重定位的含义与作用

重定位时链接器在合并多个目标文件时,修正符号引用地址的过程。目标文件.o中的代码和数据地址是临时地址(基于偏移量的),链接器需要将其调整为最终可执行文件的绝对地址。

重定位表:每个目标文件都有一个重定位表,记录了需要修正的位置及其规则:

  • 需要修正的偏移量:在目标文件中的位置;
  • 符号名称:需要修正为哪个符号的地址;
  • 重定位类型:如何计算最终地址,如相对地址或绝对地址。

重定位的作用:

  • 合并多目标文件:将分散在多个.o文件中的代码和数据分配到统一的内存布局中;
  • 解析外部依赖:将未定义的符号绑定到库中的实际地址;
  • 生成可执行文件:确保程序运行时,所有指令和数据的地址正确。

2.3 符号表和重定位在整个编译过程中的作用

编译阶段会生成符号表并记录重定位信息:

  • 生成符号表:编译器为每个.c文件生成.o目标文件,包含符号表和代码;
  • 记录重定位信息:编译器标记所有需要重定位的位置,如外部函数调用等。

链接阶段会进行符号解析以及地址分配与重定位:

  • 符号解析:链接器会检查所有目标文件的符号表,确保每个符号有且仅有一个定义;
  • 地址分配与重定位:链接器为所有符号分配最终地址,并修正代码中的引用。

即符号表是程序符号的地址簿,记录了符号的定义和引用;重定位是链接器的修正工具,确保程序可以正确访问所有符号。

2.4 动态链接库.so和静态链接库.a

动态链接库.so文件,是在程序运行时被加载的,多个程序可共享同一个库文件,节省内存和磁盘空间。

静态链接库.a文件,是在编译时整合到可执行文件中,生成独立的可执行文件,不需要外部依赖。

静态链接库和动态链接库的主要区别:

特性静态库(.a)动态库(.so)
链接方式编译时直接嵌入到可执行文件中程序运行时动态加载
文件体积可执行文件体积较大(包含库代码)可执行文件体积较小(仅存引用)
运行时依赖无需外部库文件必须存在对应的.so文件
内存占用每个进程独立加载库代码,内存冗余多个进程共享同一份库代码,内存节省
更新维护需重新编译整个程序仅替换.so文件即可更新库功能
加载速度启动快(代码已嵌入)启动稍慢(需加载动态库)
兼容性风险无版本冲突问题需保证.so版本与程序兼容
常见使用场景嵌入式系统、独立工具、无依赖部署通用系统库(如libc)、多进程共享场景

示例:

  1. 静态链接:

    gcc main.c -o program -L/path/to/libs -lstaticlib -static
    

    -static选项表明强制静态链接所有库,包括库系统如libc
    这样生成的program不依赖任何外部库。

  2. 动态链接:

    gcc main.c -o program -L/path/to/libs -ldynamiclib
    

    默认链接动态库(优先查找.so),运行时需确保动态库在系统路径或通过LD_LIBRARY_PATH指定。

  3. 混合链接:

    gcc main.c -o program -Wl,-Bstatic -lstaticlib -Wl,-Bdynamic -ldynamiclib
    
    • -Wl,-Bstatic:指定后续库静态链接。
    • -Wl,-Bdynamic:恢复为动态链接

2.5 不要混淆.o文件和.so文件

.o文件是目标文件,通常是编译单个源文件后的输出,包含机器码和符号表,但还未经过链接,所以可能有未解析的符号

.so文件是动态链接库,是多个目标文件经过链接后生成的共享库,可以在运行时被多个程序共享。

.o文件是编译阶段的产物,.so文件是链接阶段的产物;.so文件在程序运行时加载,.o文件在链接时被合并到可执行文件或静态库中。

二者主要区别:

特性目标文件(.o)动态链接库(.so)
生成阶段编译阶段的产物(单个源文件编译后生成)链接阶段的产物(多个目标文件或源码链接生成)
内容包含单个源文件编译后的机器码、符号表、重定位信息包含多个目标文件或源码的已链接代码,具有完整的符号解析和地址分配
用途作为中间文件,供后续链接生成可执行文件或库作为共享库,供程序在运行时动态加载
依赖关系未解析的符号需在链接阶段解决符号已完全解析,但需在运行时与主程序或其他库动态绑定
文件独立性无法单独运行,需链接后使用可独立存在,但需主程序调用或动态加载
内存共享不共享,每个进程独立加载多个进程可共享同一份.so的代码段,节省内存
更新维护修改后需重新编译和链接更新.so后,主程序无需重新编译(需接口兼容)

.o文件,通过编译单个源文件生成,包含机器码、符号表和重定位信息。.o文件作为中间文件,以供后续链接器将所有.o文件合并为可执行文件或库(可被归档为静态库.a文件,本质上是多个.o的集合)

.so文件,通过链接多个目标文件或源码生成,包含完全链接的代码(所有的符号已解析,除非依赖其他动态库)、位置无关代码(代码可加载到任意内存地址运行)、导出符号表(声明库中可供外部调用的函数或变量)。.so文件是在程序运行时由动态链接器加载到内存的,多个程序可共享一份.so代码,减少内存占用,同时支持库的热更新(即替换.so文件后重启程序生效)

总结:

  • .o文件是编译阶段的中间产物,用于后续链接;.so文件链接后的动态库,用于运行时共享。
  • .o文件聚焦单个模块的编译结果,.so文件聚焦多模块的协作与动态加载。

3. 知识补充

【GCC】gcc编译学习
【GDB】gdb使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

游戏引擎学习第251天:完成调试层级结构

运行游戏,查看当前调试层级的状态。 我们正在直播中开发一个完整的游戏,目前正进行调试代码的整理和清理工作。现在我们直接进入正题,虽然还不完全确定今天要完成哪些具体内容,但有几个明确的目标: 首先,…

关于Python:9. 深入理解Python运行机制

一、Python内存管理(引用计数、垃圾回收) Python(CPython)采用的是: “引用计数为主,垃圾回收为辅” 的内存管理机制。 也就是说: 引用计数机制:负责大部分内存释放,简…

【STM32单片机】#13 RTC实时时钟

主要参考学习资料: B站江协科技 STM32入门教程-2023版 细致讲解 中文字幕 开发资料下载链接:https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwddspb 单片机套装:STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协 目录 Uni…

SecureCRT 使用指南:安装、设置与高效操作

目录 一、SecureCRT 简介 1.1 什么是 SecureCRT? 1.2 核心功能亮点 1.3 软件特点 二、SecureCRT 安装与激活 2.1 安装步骤(Windows 系统) 2.2 激活与破解(仅供学习参考) 三、基础配置与优化 3.1 界面与编码设…

3.5/Q1,GBD数据库最新一区文章解读

文章题目:Global burden of low vision and blindness due to age-related macular degeneration from 1990 to 2021 and projections for 2050 DOI:10.1186/s12889-024-21047-x 中文标题:1990年至2021年因年龄相关性黄斑变性导致的低视力和失…

【Hive入门】Hive安全管理与权限控制:基于SQL标准的授权GRANT REVOKE深度解析

目录 引言 1 Hive权限模型概述 2 SQL标准授权基础 2.1 核心概念解析 2.2 授权模型工作流程 3 GRANT/REVOKE语法详解 3.1 基础授权语法 3.2 权限回收语法 3.3 参数说明 4 授权场景 4.1 基础授权示例 4.2 列级权限控制 4.3 视图权限管理 5 权限查询与验证 5.1 查看…

无缝监控:利用 AWS X-Ray 增强 S3 跨账户复制的可见性

您准备好提升您的云和 DevOps 技能了吗? 🐥《云原生devops》专门为您打造,我们精心打造的 30 篇文章库,这些文章涵盖了 Azure、AWS 和 DevOps 方法论的众多重要主题。无论您是希望精进专业知识的资深专业人士,还是渴望学习相关知识的新手,这套资源库都能满足您的需求。 …

Python Cookbook-7.2 使用 pickle 和 cPickle 模块序列化数据

任务 你想以某种可以接受的速度序列化和重建Python 数据结构,这些数据既包括基本Python 对象也包括类和实例。 解决方案 如果你不想假设你的数据完全由基本 Python 对象组成,或者需要在不同的 Python 版本之间移植,再或者需要将序列化后的…

2025.5.5总结

今日感悟:这假期就这样结束了,玩了一次滑板,打扫了一次租房,出去逛了一次街,看完了一本书,追了一部剧。既没有家人,也没有能一同畅饮的同学,更没有对象,显得确实有些孤独…

MySQL | DQL语句-连接查询

MySQL | DQL语句-连接查询 🪄个人博客:https://vite.xingji.fun 什么是连接查询 从一张表中查询数据称为单表查询。从两张或更多张表中联合查询数据称为多表查询,又叫做连接查询。什么时候需要使用连接查询? 比如这样的需求&…

Vite简单介绍

Vite 是一个现代化的前端构建工具,由 Vue.js 的创始人 Evan You 开发,旨在提供更快的开发体验和更高效的构建流程。它的名字来源于法语单词“vite”,意为“快速”,这也反映了它的核心优势——极速的冷启动和热模块替换&#xff08…

C语言-回调函数

回调函数 通过函数指针调用函数,而这个被调用的函数称为回调函数 回调函数是C语言中一种强大的机制,允许将函数作为参数传递给其他函数,从而在特定时机由后者调用。它的核心在于函数指针的使用 以下是回调函数的使用例子 先创建好一个函数…

启发式算法-禁忌搜索算法

禁忌搜索是一种可以用于解决组合优化问题的启发式算法,通过引入记忆机制跳出局部最优,避免重复搜索。该算法从一个初始解开始,通过邻域搜索策略来寻找当前解的邻域解,并在邻域解中选择一个最优解作为下一次迭代的当前解&#xff0…

Python 整理3种查看神经网络结构的方法

1. 网络结构代码 import torch import torch.nn as nn# 定义Actor-Critic模型 class ActorCritic(nn.Module):def __init__(self, state_dim, action_dim):super(ActorCritic, self).__init__()self.actor nn.Sequential(# 全连接层,输入维度为 state_dim&#xf…

Linux 查询CPU飙高的原因

获取进程ID ps -efgrep xxxx查询占用最高的线程ID top -Hp 线程ID线程ID 转 16进制数 printf 0x%x\n 线程ID基于jstack工具 跟踪堆栈定位代码位置 jstack 进程ID | grep 16禁止线程ID -A 20

Oracle OCP认证考试考点详解083系列09

题记: 本系列主要讲解Oracle OCP认证考试考点(题目),适用于19C/21C,跟着学OCP考试必过。 41. 第41题: 题目 解析及答案: 关于应用程序容器,以下哪三项是正确的? A) 它可以包含单个…

GESP2024年3月认证C++八级( 第二部分判断题(1-5))

孙子定理参考程序&#xff1a; #include <iostream> #include <vector> using namespace std;// 扩展欧几里得算法&#xff1a;用于求逆元 int extendedGCD(int a, int b, int &x, int &y) {if (b 0) {x 1; y 0;return a;}int x1, y1;int gcd extende…

C 语言比较运算符:程序如何做出“判断”?

各类资料学习下载合集 ​​https://pan.quark.cn/s/8c91ccb5a474​​ 在编写程序时,我们经常需要根据不同的条件来执行不同的代码。比如,如果一个分数大于 60 分,就判断为及格;如果用户的年龄小于 18 岁,就禁止访问某个内容等等。这些“判断”的核心,就依赖于程序能够比…

WITH在MYSQL中的用法

WITH 子句&#xff08;也称为公共表表达式&#xff0c;Common Table Expression&#xff0c;简称 CTE&#xff09;是 SQL 中一种强大的查询构建工具&#xff0c;它可以显著提高复杂查询的可读性和可维护性。 一、基本语法结构 WITH cte_name AS (SELECT ... -- 定义CTE的查询…

多序列比对软件MAFFT介绍

MAFFT(Multiple Alignment using Fast Fourier Transform)是一款广泛使用且高效的多序列比对软件,由日本京都大学的Katoh Kazutaka等人开发,最早发布于2002年,并持续迭代优化至今。 它支持从几十条到上万条核酸或蛋白质序列的快速比对,同时在准确率和计算效率之间提供灵…