编译和链接(基础速通版)

翻译环境和运行环境

前提解释

电脑是不能直接执行C语言的程序代码的,所依赖的是翻译环境进行一个源代码运行的时候需要经过翻译环境和运行环境的处理,才能得到你需要的可运行程序。

这里是源文件,也就是

.c文件,通过翻译环境得到可执行程序。

那么翻译环境里面都有什么呢,下面会进行讲解。 

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

翻译环境

总的流程 

在计算机科学中,"翻译环境"(Translation Environment)通常指的是一个用于将源代码转换为机器代码的软件环境。这通常包括编译器、汇编器、链接器等工具,以及用于编写、调试和测试代码的集成开发环境(IDE)。
翻译环境的主要作用是将高级编程语言(如C、C++、Java等)编写的源代码转换为计算机能够理解的机器代码。这个过程通常包括以下几个步骤:
1. **预处理**:处理源代码中的预处理器指令,如包含头文件、宏替换等。
2. **编译**:将源代码编译成汇编代码。
3. **汇编**:将汇编代码转换成机器代码。
4. **链接**:将多个机器代码文件合并成一个可执行文件,并处理外部依赖。
运行环境(Runtime Environment)则是指程序运行时所需的环境,包括操作系统、硬件资源、库文件、配置文件等。它为程序提供了一个执行的环境,使得程序能够正确地运行。
例如,当您运行一个用C语言编写的程序时,翻译环境包括编译器(如GCC)、链接器(如ld),以及可能使用的IDE(如Visual Studio Code、Eclipse等)。运行环境则包括操作系统(如Windows、macOS、Linux)和必要的库文件。
总的来说,翻译环境负责将源代码转换为机器代码,而运行环境负责在实际硬件上运行这个机器代码。
 

—————————————————————————————————————————— 

举例详解

exe(可执行程序)依赖翻译环境,生成的可执行程序

翻译环境依赖的也就是我们的编译器

也就是vs,vs也就是我用的编译器,以及你们使用的编译器。这个可以是不一样的。

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— 

翻译环境的组成 

翻译环境是由两个程序组成,先编译 再链接、

图片里面的obj也就是称之为目标文件

翻译环境的过程

1,我们写的代码先创建文件test.c的文件,也就是test.c,然后经过编译器的处理,生成目标文件(test.obj)也就是(obj)

2,目标文件和链接库一起经过链接器的处理,生成可执行程序。

3,生成目标文件(obj)的过程称之为编译(也就是生成obj的过程)

4,生成可执行程序的过程称之为链接。

5,最后整个我们写代码的地方也就是集成开发环境

翻译环境的须知

多个.c⽂件单独经过编译器,编译处理⽣成对应的⽬标⽂件。
注:在Windows环境下的⽬标⽂件的后缀是 .obj ,Linux环境下⽬标⽂件的后缀是 .o
多个⽬标⽂件和链接库⼀起经过链接器处理⽣成最终的可执⾏程序。
链接库是指运⾏时库(它是⽀持程序运⾏的基本函数集合)或者第三⽅库。

一个编译器里面可以有多个.c文件

—————————————————————————————————————————— 

代码举例

也就是几个文件相互协同,一个负责实现,一个负责定义,就比如

这里只需要进行声明就可以了

这说明每个源文件都是会通过编译器单独处理,生成目标文件

目标文件和链接库一块处理,通过链接器生成可执行程序

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— 

翻译环境里面的编译分为三个过程:预处理,编译,汇编

c代码->预处理->编译->汇编->链接->可执行程序

翻译流程

1,首先创建一个文件test.c

2.经过预处理(预编译)(-E), 生成后缀为test.i 的文件

3.test.i经过 编译(-O),生成后缀为test.S的文件

4.test.s经过汇编,生成目标文件test.o(obj)的文件。

5.此时的目标文件和链接库一块处理,通过链接器进过链接生成可执行程序。

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— 

预处理阶段(预编译)

预处理的流程

预处理阶段也叫预编译

test.c预处理 -E 处理之后生成test.i

这里是test.c -E(也就预处理之后)

我需要生成一个.i的文件

会发现这里.i里面,已经进行处理了

注释在.c里面有 但是子啊.i里面是没有的

注释是给人看的,计算机是看不懂的 也不会进行读取的

通常 编译器编译的时候,中间文件直接都删掉了

——————————————————————————————————————————  

预处理阶段规则:

预处理阶段主要处理那些源⽂件中#开始的预编译指令。⽐如:#include,#define,处理的规则如下:

• 将所有的 #define 删除,并展开所有的宏定义。

• 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。

• 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置。这个过程是递归进

⾏的,也就是说被包含的头⽂件也可能包含其他⽂件。

• 删除所有的注释

• 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等。

• 或保留所有的#pragma的编译器指令,编译器后续会使⽤。

经过预处理后的.i⽂件中不再包含宏定义,因为宏已经被展开。并且包含的头⽂件都被插⼊到.i⽂件

中。所以当我们⽆法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的.i⽂件来确认。

编译器默认的时候,进行编译的时候,编译完毕就会直接删掉了

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— 

编译

编译流程 

也就是预处理的下一步

gcc -S test.i -o test.s

也就是原来的是 test.i(预处理之后生成的文件) -o(进行编译)生成test.s文件

编译期间会进行词法分析、语法分析、语义分析及优化

编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件。

编译这个动作是会生成.s的文件的

这个文件里面放置的是汇编代码

test.S里面放置的是汇编代码

gcc(这里使用的是gcc编译器)-s也就是对预处理之后的test,i文件进行处理,生成test.s文件

—————————————————————————————————————————— 

词法分析 

test.s文件生成之前会进行 词法分析、语法分析、语义分析及优化 

拿这个代码举例:array[index] = (index+4)*(2+6);

将源代码程序被输⼊扫描器,扫描器的任务就是简单的进⾏词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)

也就是对代码进行拆分和解析,上⾯程序进⾏词法分析后得到了16个记号

简单的说就是看看文件里面有什么,下一步我得干嘛,顺便看看这个符号都有啥,方便等会转化 

——————————————————————————————————————————  

语法分析

生成语法树(可以理解为计算逻辑应该是什么,怎么进行计算,是先进行加法还是先减法,罗列好,方便下一步的运行)

这里整体根据表达式 进行语法分析

—————————————————————————————————————————— 

语义分析

什么是语义分析(简单的说就是二次检查语法错误,看看整形+整数是不是整形,不是的话提前报错)

由语义分析器来完成语义分析,即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分

析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。

就比如这个,这里是进行语义分析,分析出来是4 这个是整形+整形 本质是整形

这就是我们说的语义分析

 ——————————————————————————————————————————  ——————————————————————————————————————————  ——————————————————————————————————————————  —————————————————————————————————————————— 

汇编 

汇编的流程 

gcc -c(汇编)生成test.o生成汇编文件

汇编也就是把汇编转化成二进制指令 

下一步就可以把目标文件通过链接库+链接器生成可执行程序了

——————————————————————————————————————————  

目标文件的产生流程

汇编会生成.o 文件,test.o文件里面放置的是二进制,这个文件就是我们的目标文件

此时,这个整个大的过程叫做编译,也就是包括了预处理+编译+汇编

包括了

源文件(test.c)->(-E)->预处理->(处理不必要的东西)->编译(test.i)->(-O(词法分析)(语法分析)(语义分析))-->汇编(test.s)(目标文件)

在.o这个文件里面 已经放置的是二进制文件了

汇编就是把计算机代码转换成计算机指令

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— ——————————————————————————————————————————  

链接

什么是链接

链接是⼀个复杂的过程,链接的时候需要把⼀堆⽂件链接在⼀起才⽣成可执⾏程序。

链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。

链接解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题

——————————————————————————————————————————   

链接的过程

链接的时候是把多个文件放到一起,生成一个可执行程序

符号表也是需要合并的,符号表也是需要合并成一份

这里合并的时候 ,保留有效的,舍弃无效的,对符号表进行合并

合并完成之后,我们通过有效的地址找到文件,从而相互调用函数

——————————————————————————————————————————    

错误举例

如果此时你写错变量名字了

也就是Add写成add了 此时报错

报错的就是链接错误

链接错误就是这个链接表对照的时候 找不到地址

这就是连接错误的由来

查找的时候是按照大写去查找的,但是实际找到的时候是按照小写着找到的

最后发现,链接表合并结束,对照不上。

由此而来,链接错误。

——————————————————————————————————————————   

二次讲解 

我们已经知道,每个源⽂件都是单独经过编译器处理⽣成对应的⽬标⽂件。

test.c 经过编译器处理⽣成 test.o

add.c 经过编译器处理⽣成 add.o

我们在 test.c 的⽂件中使⽤了 add.c ⽂件中的 Add 函数和 g_val 变量。

我们在 test.c ⽂件中每⼀次使⽤ Add 函数和 g_val 的时候必须确切的知道 Add 和 g_val 的地

址,但是由于每个⽂件是单独编译的,在编译器编译 test.c 的时候并不知道 Add 函数和 g_val

变量的地址,所以暂时把调⽤ Add 的指令的⽬标地址和 g_val 的地址搁置。等待最后链接的时候由

链接器根据引⽤的符号 Add 在其他模块中查找 Add 函数的地址,然后将 test.c 中所有引⽤到

Add 的指令重新修正,让他们的⽬标地址为真正的 Add 函数的地址,对于全局变量 g_val 也是类

似的⽅法来修正地址。这个地址修正的过程也被叫做:重定位。

前⾯我们⾮常简洁的讲解了⼀个C的程序是如何编译和链接,到最终⽣成可执⾏程序的过程,其实很多

内部的细节⽆法展开讲解。

⽐如:⽬标⽂件的格式elf,链接底层实现中的空间与地址分配,符号解析

和重定位等,如果你有兴趣,可以看《程序的⾃我修养》⼀书来详细了解

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— ——————————————————————————————————————————  

运行环境

运行环境的理解和分析

1. 程序必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序的载⼊必须由⼿⼯安排,也可能是通过可执⾏代码置⼊只读内存来完成。(当我们运行程序的时候,我们是双击运行程序,也就是加载到内存里面,这样的操作有些是操作系统完成,有些是手工安排。)

2. 程序的执⾏便开始。接着便调⽤main函数。(此时放到内存里面之后开始只执行。)

3. 开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使⽤静态(static)内存,存储于静态内存中的变量在程序的整个执⾏过程⼀直保留他们的值。(软件运行,程序运行依赖的环境)

4. 终⽌程序。正常终⽌main函数;也有可能是意外终⽌。(软件的关闭)

这里说明一下,这既是一个过程,学会理解就好了,不要较真的不得了。

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— ——————————————————————————————————————————  

总结

翻译流程

1,首先创建一个文件test.c

2.经过预处理(预编译)(-E), 生成后缀为test.i 的文件

3.test.i经过 编译(-O),生成后缀为test.S的文件

4.test.s经过汇编,生成目标文件test.o(obj)的文件。

5.此时的目标文件和链接库一块处理,通过链接器进过链接生成可执行程序。

总的流程

源文件(test.c)->(-E)->预处理->(处理不必要的东西)->编译(test.i)->(-O(词法分析)(语法分析)(语义分析))-->汇编(test.s)(目标文件) 

—————————————————————————————————————————— —————————————————————————————————————————— —————————————————————————————————————————— ——————————————————————————————————————————   

编辑、预处理、编译、链接之间的区别

预处理只会处理#开头的语句,编译阶段只校验语法,链接时才会去找实体,所以是链接时出错的,故选C。这里附上每个步骤的具体操作方式:

预处理:相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有头文件(都已经被展开了)、宏定义(都已经替换了),没有条件编译指令(该屏蔽的都屏蔽掉了),没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

编译:将预处理完的文件逐一进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。编译是针对单个文件编译的,只校验本文件的语法是否有问题,不负责寻找实体。

链接:通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。需要注意的是,链接阶段只会链接调用了的函数/全局变量,如果存在一个不存在实体的声明(函数声明、全局变量的外部声明),但没有被调用,依然是可以正常编译执行的。

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

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

相关文章

图解PyTorch中的torch.gather函数和 scatter 函数

前言 torch.gather在目前基于 transformer or query based 的目标检测中,在最后获取目标结果时,经常用到。 这里记录下用法,防止之后又忘了。 介绍 torch.gather 官方文档对torch.gather()的定义非常简洁 定义:从原tensor中获…

Ubuntu joystick 测试手柄 xbox

Ubuntu joystick 测试手柄 xbox 测试使用Ubuntu20.04 测试环境在工控机 安装测试 实际测试使用的手柄是北通阿修罗2pro 兼容xbox Ubuntu20.04主机 连接手柄或者无线接收器后查看是否已经检测到: ls /dev/input找到输入中的 js0 即为手柄输入 需要安装joysti…

注意力机制篇 | YOLOv8改进之添加DAT注意力机制

前言:Hello大家好,我是小哥谈。DAT(Vision Transformer with Deformable Attention)是一种引入了可变形注意力机制的视觉Transformer。在训练算法模型的时候,通过引入可变形注意力机制,改进了视觉Transformer的效率和性能,使其在处理复杂的视觉任务时更加高效和准确。�…

css酷炫边框

边框一 .leftClass {background: #000;/* -webkit-animation: twinkling 1s infinite ease-in-out; 1秒钟的开始结束都慢的无限次动画 */ } .leftClass::before {content: "";width: 104%;height: 102%;border-radius: 8px;background-image: linear-gradient(var(…

正则表达式引擎库汇合

1.总览表格 一些正则表达式库的对比 index库名编程语言说明代码示例编译指令1Posix正则C语言是C标准库中用于编译POSIX风格的正则表达式库 posix-re.cgcc posix-re.c 2PCRE库C语言提供类似Perl语言的一个正则表达式引擎库。 一般系统上对应/usr/lib64/libpcre.so这个库文件&am…

柔性数组详细讲解

动态内存函数的使用和综合实践-malloc,free,realloc,calloc-CSDN博客https://blog.csdn.net/Jason_from_China/article/details/137075045 柔性数组存在的意义 柔性数组在编程语言中指的是可以动态调整大小的数组。相比固定大小的数组&#…

STL容器的一些操作(常用的,不全)

目录 string 1.string的一些创建 2.string 的读入和输出: 3.string的一些操作 4.彻底清空string 容器的函数 vector 1.vector的一些创建: 2.vector的一些操作: 3.vector的彻底清空并释放内存: queue 循环队列&#xff1…

兑换码生成算法

兑换码生成算法 兑换码生成算法1.兑换码的需求2.算法分析2.重兑校验算法3.防刷校验算法 3.算法实现 兑换码生成算法 兑换码生成通常涉及在特定场景下为用户提供特定产品或服务的权益或礼品,典型的应用场景包括优惠券、礼品卡、会员权益等。 1.兑换码的需求 要求如…

Pointnet++分类和分割数据集准备和实验复现

5.分类数据集Modelnet40及可视化 Modelnet40分类数据集 原始的modelnet40是off文件,是cad模型 OFF文件是一种用于存储三维对象信息的文件格式,全称为"Object File Format"。它主要用于存储几何体的顶点、边和面信息,以及可能的颜…

面对复杂多变的网络攻击,企业应如何守护网络安全

企业上云,即越来越多的企业把业务和数据,迁移到云端。随着云计算、大数据、物联网、人工智能等技术的发展,用户、应用程序和数据无处不在,企业之间的业务边界逐渐被打破,网络攻击愈演愈烈,手段更为多。 当前…

uni app 扫雷

闲来无聊。做个扫雷玩玩吧&#xff0c;点击打开&#xff0c;长按标记&#xff0c;标记的点击两次或长按取消标记。所有打开结束 <template><view class"page_main"><view class"add_button" style"width: 100vw; margin-bottom: 20r…

Docker容器监控之CAdvisor+InfluxDB+Granfana

介绍&#xff1a;CAdvisor监控收集InfluxDB存储数据Granfana展示图表 目录 1、新建3件套组合的docker-compose.yml 2、查看三个服务容器是否启动 3、浏览cAdvisor收集服务&#xff0c;http://ip:8080/ 4、浏览influxdb存储服务&#xff0c;http://ip:8083/ 5、浏览grafan…

如何利用CSS实现文字滚动效果

1. 使用CSS3的animation属性 CSS3的animation属性可以让元素在一段时间内不停地播放某个动画效果。我们可以利用这个特性来实现文字滚动效果。 我们需要定义一个包含所有需要滚动的文本的容器元素。比如&#xff1a; <div class"scroll-container"><p>…

JAV八股--redis

如何保证Redis和数据库数据一致性 关于异步通知中消息队列和Canal的内容。 redisson实现的分布式锁的主从一致性 明天继续深入看这个系列问题 介绍IO复用模型

【机器学习300问】59、计算图是如何帮助人们理解反向传播的?

在学习神经网络的时候&#xff0c;势必会学到误差反向传播&#xff0c;它对于神经网络的意义极其重大&#xff0c;它是训练多层前馈神经网络的核心算法&#xff0c;也是机器学习和深度学习领域中最为重要的算法之一。要正确理解误差反向传播&#xff0c;不妨借助一个工具——计…

代码随想录算法训练营第24天|理论基础 |77. 组合

理论基础 jia其实在讲解二叉树的时候&#xff0c;就给大家介绍过回溯&#xff0c;这次正式开启回溯算法&#xff0c;大家可以先看视频&#xff0c;对回溯算法有一个整体的了解。 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法&#xff08;理…

深入理解数据结构——堆

前言&#xff1a; 在前面我们已经学习了数据结构的基础操作&#xff1a;顺序表和链表及其相关内容&#xff0c;今天我们来学一点有些难度的知识——数据结构中的二叉树&#xff0c;今天我们先来学习二叉树中堆的知识&#xff0c;这部分内容还是非常有意思的&#xff0c;下面我们…

前端秘法番外篇----学完Web API,前端才能算真正的入门

目录 一.引言 二.元素的获取和事件 1.获取元素 2.各种事件 2.1点击事件 2.2键盘事件 三.获取&修改操作 1.获取修改元素属性 2.修改表单属性 2.1暂停播放键的转换 2.2计数器的实现 2.3全选的实现 3.样式操作 3.1行内样式操作 3.2类名样式操作 四.节点 1.创…

记录Xshell使用ed25519公钥免密链接SSH

试了半天&#xff0c;Xshell好像没办法导入linux生成的ssh公钥,因此需要以下步骤实现免密登录 结论&#xff0c;在linux公钥文件中&#xff0c;将客户端生成的ed25519公钥加上去即可(一个公钥单独一行) 1.使用Linux生成秘钥文件(不需要输入私钥密码passphrase)或者直接创建一…

【Servlet】继承关系以及service方法

文章目录 一、继承关系二、相关方法 一、继承关系 Servlet接口下有一个GenericServlet抽象类。在GenericServlet下有一个子类HttpServlet&#xff0c;它是基于http协议。 继承关系 javax.servlet.Servlet接口​ javax.GenericServlet抽象类​ javax.servlet.http.HttpServ…