C/C++ - 从代码到可执行程序的过程

news2024/11/19 9:19:39

(1)预编译

主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下:

  1. 删除所有的#define,展开所有的宏定义。
  2. 处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
  3. 处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其

    文件。
  4. 删除所有的注释,“//”和“/**/”。
  5. 保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件
    被重
  6. 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是
    能够显示行号。

(2)编译

把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应
的汇编代码文件。

  1. 词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分
    割成一系列的记号。
  2. 语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的
    语法树是一种以表达式为节点的树。
  3. 语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进
    行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能
    确定
    的语义。
  4. 优化:源代码级别的一个优化过程。
  5. 目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语
    言表示。
  6. 目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移
    来替代乘法运算、删除多余的指令等。

(3)汇编

将汇编代码转变成机器可以执行的指令(机器码文件)。 汇编器的汇编过程相对于编译器来说更简单,没
有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过
来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Windows 下)、xxx.obj(Linux下)。

(4)链接

将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链
接:

静态链接
函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库
中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。

以下面这个图来简单说明一下从静态链接到可执行文件的过程,根据在源文件中包含的头文件和程序中使用到的库函数,如stdio.h中定义的printf()函数,在libc.a中找到目标文件printf.o(这里暂且不考虑printf()函数的依赖关系),然后将这个目标文件和我们hello.o这个文件进行链接形成我们的可执行文件。
在这里插入图片描述
这里有一个小问题,就是从上面的图中可以看到静态运行库里面的一个目标文件只包含一个函数,如libc.a里面的printf.o只有printf()函数,strlen.o里面只有strlen()函数。

我们知道,链接器在链接静态链接库的时候是以目标文件为单位的。比如我们引用了静态库中的printf()函数,那么链接器就会把库中包含printf()函数的那个目标文件链接进来,如果很多函数都放在一个目标文件中,很可能很多没用的函数都被一起链接进了输出结果中。由于运行库有成百上千个函数,数量非常庞大,每个函数独立地放在一个目标文件中可以尽量减少空间的浪费,那些没有被用到的目标文件就不要链接到最终的输出文件中。

缺点:
空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个
目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;
更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。
运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,
在执行的时候运行速度快。

动态链接

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形
成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

假设现在有两个程序program1.o和program2.o,这两者共用同一个库lib.o,假设首先运行程序program1,系统首先加载program1.o,当系统发现program1.o中用到了lib.o,即program1.o依赖于lib.o,那么系统接着加载lib.o,如果program1.o和lib.o还依赖于其他目标文件,则依次全部加载到内存中。当program2运行时,同样的加载program2.o,然后发现program2.o依赖于lib.o,但是此时lib.o已经存在于内存中,这个时候就不再进行重新加载,而是将内存中已经存在的lib.o映射到program2的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。

优点:
共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副
本,而是这多个程序在执行时共享同一份副本;更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运
行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。
性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损
失。

生成可执行文件

什么情况会编译成功但链接失败

(1)说明并使用了类型、函数、变量,但没给出相应类型、函数或变量的定义。关于说明和定义的区别见上述教程,说明可以通过#include头文件进行,也可以直接进行说明如int f( )或extern int f( )。若同时给出函数f的函数体则称为定义。对于变量若有初始值就算定义,例如"extern int x=3; "便是定义,这种语法有其独特应用背景。

(2)对标准库的连接失败,例如调用了sin(double x),却找不到数学运算标准库进行连接了,可能是安装后因各种可能原因被删除了。

(3)全局变量和静态变量存放在数据段,而固定大小的数据段不够用了,例如定义太多的全局变量和静态变量,甚至巨型数组全局或静态变量。

(4)数据段被偷用后无法容纳较多的全局变量和静态变量,这种错误最难发现且最难改正。主要是一些常量在偷用数据段,例如字符串常量"abc"等等,其它诸多情形参见上述教程。另外虚函数入口地址表等也会偷用。

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

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

相关文章

简单工厂模式

简单工厂模式所谓组件:从设计上讲,组件就是能完成一定功能的封装体。小到一个类,大到一个系统,都可以称为组件,因为一个小系统放到更大的系统里面去,也就当个组件而已。模式定义:提供一个创建对…

servlet运用自定义分发优化servlet泛滥

servlet优化 Web 层的 Servlet 个数太多了,不利于管理和编写 我们发现每一个功能都需要定义一个 servlet,一个模块需要实现增删改查功能,就需要4个 servlet,模块一多就会造成servlet 泛滥。此时我们就想 servlet 能不能像 servi…

YOLOv6 训练自己的数据集

项目地址:https://github.com/meituan/YOLOv6 论文地址:https://arxiv.org/abs/2209.02976 论文解析:http://t.csdn.cn/0ZQbV YOLOv6 是一种专为工业应用设计的单级对象检测框架,具有硬件友好的高效设计和高性能。YOLOv6-N 在 NVI…

【windows】docker与docker-compose部署spring boot项目

看完不会用,我倒立**,保姆级教学 docker部署项目 采用Dockerfile部署 docker-compose部署项目 docker-compose部署,实际上是对容器的编排,以及容器间的一些依赖 比如一个springboot项目,需要使用redis,…

深入 Redis sds

文末有视频讲解 在上一个模块中,我和小伙伴们一起学习了 Redis 最核心的命令,主要涉及 String、List、Hash、Set、Sorted Set 五种数据结构的命令,同时,我们还介绍了每种数据结构的实战场景,并带领小伙伴们使用 Java 语…

11、ThingsBoard-租户配置

1、概述 租户配置(tenant profile)如其名是租户相关的配置,通俗一点就是给你这个租户的功能增加一些限制,如果你加钱,我就给你把限制设置高一点,thingsboard官方那个收费的版本不就是这样的吗?租户配置在系统层,系统管理员可以创建租户配置,然后使用租户配置为多个租…

centos7安装kubeadm

centos7安装kubeadm 一、基础设置 1、设置主机名 hostnamectl set-hostname master hostnamectl set-hostname node01vim /etc/hosts 192.168.198.169 master 192.168.198.170 note01hostnamectl hostnamectl 是在 centos7 中新增加的命令,它是是用来管理给定主机…

2023年我花费数小时整理的Java常用类进阶学习文档,你学会了吗?

文章目录1. 基本类型的包装类1.1 概念1.2 常用的属性1.3 常用的方法1.4 自动装箱和自动拆箱2. 字符串类2.1 String 类2.2 StringBuilder类2.3 StringBuffer类2.4 StringBuilder 的扩容机制3. 数字常用类3.1 Math 类3.2 Radom 类4. 枚举类5. 日期类5.1 Date 类5.2 DateFormat 类…

基于微信小程序的小区租拼车管理信息系统小程序

文末联系获取源码 开发语言:Java 框架:ssm JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏览器…

学习理解10G Ethernet Subsystem之IEEE1588

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 学习理解10G Ethernet Subsystem之IEEE1588前言原理简介one step/two step1PPSIP 设置前言 1588大多是走在报文中,主要通过一些报文交互来完成同步过程&#xff…

在Docker中安装Gitea

目录在Docker中安装Gitea1、拉取最新Gitea官方镜像2、实例化一个Gitea容器3、Gitea需要数据源,因此使用mysql作为后端数据库4、在mysql中创建一个新数据库,起名gitea5、访问Gitea主页http://host:3000,进入初始配置页在Docker中安装Gitea 1、…

【自学Python】Python位运算符

Python位运算符 Python位运算符教程 在 Python 中,位运算符主要是用于 数值类型 的二进制的运算。 Python位运算符语法 位运算符说 明案例备注&按位与a & b返回 a 和 b 相与的结果|按位或a | b返回 a 和 b 相或的结果^按位异或a ^ b返回 a 和 b 相异或的…

FFMpeg 实现视频编码、解码

FFMpeg 作为音视频领域的开源工具,它几乎可以实现所有针对音视频的处理,本文主要利用 FFMpeg 官方提供的 SDK 实现音视频最简单的几个实例:编码、解码、封装、解封装、转码、缩放以及添加水印。 接下来会由发现问题->…

Java中的equals()方法和hashCode的关系

文章目录1.Java中equals()方法比较的是什么?2.equals方法和hashcode的关系3.什么是hashCode3.1 hashcode有什么作用呢?4.关于重写equals()方法的两条规范5.代码实例1.Java中equals()方法比较的是什么? 最直接的回答就是看调用equals()方法的…

联合评测 DapuStor Roealsen5 NVMe SSD在GreatSQL数据据库中的应用探索

1、合作背景 万里开源软件有限公司 ​ 北京万里开源软件有限公司,是专注于国产自主可控数据库产品研发超 20年的国家高新技术企业,参与多个国家级的数据库行业标准制定工作。本次用于测试的 GreatSQL 开源数据库是适用于金融级应用的国内自主 MySQL 版…

Redis 的基础数据结构(一) 可变字符串、链表、字典

这周开始学习 Redis,看看Redis是怎么实现的。所以会写一系列关于 Redis的文章。这篇文章关于 Redis 的基础数据。阅读这篇文章你可以了解: 动态字符串(SDS)链表字典 三个数据结构 Redis 是怎么实现的。 SDS SDS (S…

从0到1完成一个Node后端(express)项目(二、下载数据库、navicat、express连接数据库)

往期 从0到1完成一个Node后端(express)项目(一、初始化项目、安装nodemon) 下载MySQL数据库(PHPstudy) 我们这里不采用官网下载MySQL的方式、因为启动不方便,而且多版本的MySQL大家也不好去管…

【MyBatis】| MyBatis概述、MyBatis⼊⻔程序

一、MyBatis概述1. 框架在⽂献中framework被翻译为框架。Java常⽤框架:SSM三⼤框架:Spring SpringMVC MyBatisSpringBootSpringCloud等。。。。框架其实就是对通⽤代码的封装,提前写好了⼀堆接⼝和类,我们可以在做项⽬的时候直接…

Frida零基础入门教程

阅读这篇文章,不仅能了解frida是什么,还能知道如何搭建Frida运行换以及学会用frida进行简单的java/native hook实战。 Xposed大家不陌生,在手机上运行的Hook框架,Xposed插件编写完成并在手机上通过hook框架加载,打开指定应用就能实现代码注入,也就是说Xposed插件的代码是…

FFmpeg进行笔记本摄像头+麦克风实现流媒体直播服务,展示在浏览器上。

0、本文中所用软件下载包 1、前置工作 1.1 下载 ffmpeg,Download FFmpeg, 1.1.1配置ffmpeg如下图 1.1.2测试ffmpeg 安装成功:ffmpeg -version 1.1.3 使用FFmpeg获取本地摄像头设备 ffmpeg -list_devices true -f dshow -i dummy video和aud…