C语言之编译和链接

目录

一、引言 

二、编译和链接

2.1 预处理(预编译)

2.2 编译

2.3 汇编

2.4 链接


一、引言 

#include <stdio.h>
int main()
{printf("hello, world\n");return 0;
}

像这样,只由ASCII字符构成的文件被称为文本文件,它需要经过翻译环境的编译和链接后才能成为二进制文件,也就是可执行程序。

可执行程序的内部是一系列二进制形式的计算机指令和数据的集合,CPU可以直接识别,但是对于程序员来说非常难以记忆和使用,所以程序员开发出了编程语言,使用这些语言来编写程序。

为了要运行环境中运行编写出来的程序,每条C语句都必须在翻译环境中被其他程序转化为一系列的汇编指令,然后这些指令按照格式打好包,并以二进制磁盘文件的形式存放起来。

在ANSI C的任何一种实现中,存在两个不同的环境

  • 翻译环境,在这个环节中源代码被转换成可执行的机器指令
  • 执行环境,用于实际执行代码

翻译环境是由编译链接两个过程组成的,而编译又可以细分为预处理(预编译)、编译和汇编三个过程

二、编译和链接

在一个项目中可能同时存在多个.c文件,这些文件经过编译器处理编译出对应的目标文件,再和链接库一起经过链接器处理生成最终的可执行程序

2.1 预处理(预编译)

在预处理阶段,源文件和头文件会被处理成以 .i 为后缀的文件

我们用gcc编译器观察一下对 test.c 文件预处理后的 .i 文件,在终端输入以下指令:

gcc -E test.c -o test.i

预处理阶段主要处理源文件中以#为开头的预编译指令,例如#include,#define,处理的规则如下

(1)将所有的#define删除,并展开所有的宏定义

图中我们可以观察到,在 test.i 中#define指令已经被删除,M也被替换成了100

(2)处理所有的条件编译指令,如 #if ,#ifdef,#elif,#else,#endif

(3)处理 #include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他头文件

(4)删除所有的注释

(5)添加行号和文件名标识,方便后续编译器生成调试信息等

(6)保留所有的 #pragma 编译器指令,因为后续编译器会用到

2.2 编译

编译器将预处理完的 .i 文本文件进行一系列的词法分析、语法分析、语义分析和优化,编译成以 .s 为后缀的汇编代码文件。汇编过程的命令如下:

gcc -S test.i -o test.s

汇编代码文件大概长这样:

假设有代码如下,编译过程该如何对其进行分析呢?

array [index] = (index+ 4 )*( 2 + 6 );

编译过程可分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成、目标代码优化。

(1)词法分析

将源代码程序输入扫描器中,扫描器将其中的字符序列分割成一系列的记号。上面的代码进行词法分析后得到了16个记号:

记号类型
array标识符
[左方括号
index标识符
]右方括号
=赋值
(左圆括号
index标识符
+加号
4数字
)右圆括号
*乘号
(左圆括号
2数字
+加号
6数字
)右圆括号

(2)语法分析

接下来,语法分析器将对扫描器生成的记号进行语法分析,产生语法树。

(3)语义分析

语义分析器对表达式进行语法层面分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配,类型转换等。这个阶段会报告错误的语法信息

2.3 汇编

汇编器将汇编代码翻译成机器可执行的指令,每一条汇编语句几乎都对应一条机器指令。在这一步中,只是根据汇编指令和机器指令的对照表一一的进行翻译,不做指令优化

汇编会生成 .o 为后缀的目标文件,命令如下:

gcc -c test.s -o test.o

汇编后的目标文件就已经是二进制形式了,我们很难阅读

2.4 链接

目标文件在经过链接之后才能称为可执行文件。既然目标文件和可执行文件都是二进制格式,为什么还要再链接一次呢?

因为编译只是将我们自己写的代码变成了二进制格式,它还需要和标准库、动态链接库等结合起来,这些组件都是程序运行所必须的。

链接过程主要包括地址和空间分配,符号决议和重定位等步骤,解决的是一个项目中多文件、多模块之间互相调用的问题。

完.

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

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

相关文章

基于 Hologres+Flink 的曹操出行实时数仓建设

本文整理自曹操出行实时计算负责人林震基于 HologresFlink 的曹操出行实时数仓建设的分享&#xff0c;内容主要分为以下六部分&#xff1a; 曹操出行业务背景介绍曹操出行业务痛点分析HologresFlink 构建企业级实时数仓曹操出行实时数仓实践曹操出行业务成果分析未来展望 一、曹…

vue3中新增时form对象无法赋值问题

有时候对象的值死活赋不上值&#xff0c;这时候可以看下赋值的对象变量名是否和页面组件中的ref相同&#xff0c; 更改后正常赋值

【spark】SparkSQL

目录 SparkSQL01.快速入门什么是SparkSQL为什么学习SparkSQLSparkSQL的特点SparkSQL发展历史-前身Shark框架SparkSQL发展历史 02.SparkSQL概述SparkSQL和Hive的异同SparkSQL的数据抽象DataFrame概述SparkSession对象 03.DataFrame入门和操作DataFrame的组成DataFrame的代码构建…

探索FTP:原理、实践与安全优化

引言 在正式开始讲解之前&#xff0c;首先来了解一下文件存储的类型有哪些。 DAS、SAN和NAS是三种不同的存储架构&#xff0c;分别用于解决不同场景下的数据存储需求。 DAS (Direct Attached Storage 直接附加存储)&#xff1a;DAS 是指将存储设备&#xff08;如硬盘&#x…

Python Django编写接口并用Jmeter测试的方法

一、环境准备 python3.6.7Pycharm 二、创建项目 我这里是在Django项目中新建了个APP&#xff0c;目录结构如下图所示&#xff1a; 那么怎么在已有的Django项目中新建APP并进行配置呢&#xff1a; 2.1、新建app a、可以在终端输入命令&#xff1a;python manage.py startap…

解密OceanBase数据库引擎:探秘数据的深海奥秘

目录 1、引言 1.1 数据库引擎的重要性 1.2 OceanBase数据库引擎的背景和意义 2、OceanBase数据库引擎的基本概述 2.1 数据库引擎的定义和功能 2.2 OceanBase数据库引擎的特点和优势 3、OceanBase数据库引擎的架构和设计 3.1 分布式架构的概念和原理 3.2 OceanBase数据…

某马头条——day05

文章定时发布 实现方案对比 实现方案 延迟队列服务实现 按照文档进行项目的导入并准备数据库表导入对应实体类和nacos配置中心 乐观锁集成 redis集成和测试 成功集成通过测试 添加任务 ①&#xff1a;拷贝mybatis-plus生成的文件&#xff0c;mapper ②&#xff1a;创建task类…

C语言编译和链接

翻译环境和运行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境 .第一种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令 .第二种是执行环境&#xff0c;它用于实际执行代码 翻译环境 翻译环境是由编译和链接两个大过程组成&#xff0c;而…

SMT回流焊工艺之回流温度曲线

引言 在SMT生产流程中&#xff0c;如何控制回焊炉的温度是非常重要的一环&#xff0c;好的炉温曲线图意味着可以形成良好的焊点。 上一期分享&#xff08;SMT回流焊温度解析之锡膏焊接特性&#xff09;中&#xff0c;我们着重介绍了SMT回流工艺中的锡膏焊接部分。本期内容主要…

docker容器和常用命令

1.什么是容器 容器是隔离的环境中运行的一个 进程 , 如果进程结束 , 容器就会停止. 细致: 容器的隔离环境 , 拥有自己的 ip 地址 , 系统文件 , 主机名 , 进程管理 , 相当于一个 mini的系统 2.容器 vs 虚拟机 3.Docker极速上手指南 #1.安装相关依赖. sudo yum install -y …

十三、Three场景物体增加发光特效

物体发光效果非常炫酷,本期来讲three场景内物体自带发光效果怎么来实现。本次使用的是threejs138版本,在vue3+vite+ant的项目中使用。 下面来看看实现的效果。绿色罐体有了明显的发光效果。 实现步骤 增加composer.js import { UnrealBloomPass } from three/examples/jsm/po…

[C++] external “C“的作用和使用场景(案例)

C++中extern "C"的作用是什么? 在 C++ 中,extern "C" 的作用是告诉编译器按照 C 语言的规范来处理函数名和变量名。这是因为 C++ 编译器会对函数名和变量名进行名称修饰(name mangling),以区分不同的函数和变量。而在 C 语言中,函数名和变量名不会被名…