一个程序从编译到运行的全过程

程序运行的具体步骤总结 1预处理(加载一些头文件、宏替换)   预处理器:将#include 包含的文件内容插入该命令处,检查全文处理#define宏定义,处理条件编译命令#if  #endif 将不需要的code屏蔽掉,处理完毕后生成修改后的源代码hello.i. 2编译(包括汇编和生成机器两部分,最终生成目标文件 .o)   编译器:词法分析语法分析,生成一条条的汇编语句如mov ax,bx,相当于是优 阅读详情
AI摘要

文章详细介绍了程序从编译到运行的过程,包括编译器处理、预处理、汇编、链接、以及载入内存时的虚拟内存和用户空间概念。区分了编译型和解释型语言,并以C语言为例解析了链接和载入的具体步骤。,

一个程序从编译到运行的全过程

每次用编译器写完一个程序后,我们会进行调试和执行,将代码的结果输出在我们的电脑屏幕上,但是我们并不清楚,为什么我们写的这些代码,可以转为我们想要的结果呢?

其实,在ANSI C(标准C)的任何一种实现中,都存在两个不同的环境,即翻译环境和执行环境。

翻译环境下,程序会经过两个阶段,一个是编译,一个是链接。翻译环境内,代码被转换为可执行的机器指令。执行环境内,它用于实际执行的代码。

那么,我们的程序就是进行了这两个过程,才最终将我们想要的结果输出在屏幕上。

一个程序,从编写完代码,到被计算机运行,总共需要经历以下四步:

  1. 编译:编译器会将程序源代码编译成汇编代码。
  2. 汇编:汇编器会将汇编代码文件翻译成为二进制的机器码。
  3. 链接:链接器会将一个个目标文件和库文件链接在一起,成为一个完整的可执行程序。
  4. 载入:加载器会将可执行文件的代码和数据从硬盘加载到内存中,然后跳转到程序的第一条指令处开始运行。

链接器和加载器是由操作系统实现的程序。而编译器和汇编器则是由不同的编程语言自己实现的了。

这里需要展开来说一说,我们常用的高级语言,按照转化成机器码的方式不同可以分为编译型语言和解释型语言:

  1. 编译型语言要求由编译器提前将源代码一次性转换成二进制指令,即生成一个可执行程序,后续的执行无需重新编译。比如我们常见的 C、Golang 等,优点是执行效率高;缺点是可执行程序不能跨平台(不同的操作系统对不同的可执行文件的内部结构要求不同;另外,由于不同操作系统支持的函数等也可能不同,所以部分源代码也不能跨平台)。
  2. 解释型语言不需要提前编译,程序只在运行时才由解释器翻译成机器码,每执行依次就要翻译一次。比如我们常见的 Python、PHP 等,优点是较方便(对编写用户而言,省去了编译的步骤),实时性高(每次修改代码后都可直接运行),能跨平台;缺点是效率低。
  3. 半编译半解释型语言:还有一类比较特殊,混合了两种方式。源代码需要先编译成一种中间文件(字节码文件),然后再将中间文件拿到虚拟机中解释执行。比如我们常见的 Java、C# 等。

所以,要设计一门语言,还必须为其编写相应的编译器和解释器,将源代码转化为计算机可执行的机器码。由于不同的语言有不同的转化方式,接下来将以最常见的 C 语言为例,简单分析一下 编译→汇编→链接→载入 的过程。

总结:不同的语言会使用不同的方式将源代码转化为机器码,但是之后的链接和载入过程都是由操作系统完成的,都是相同的。

编译

编译是读取源程序,进行词法和语法分析,将高级语言代码转换为汇编代码。整个编译过程可以分为两个阶段。

预处理

  1. 对其中的伪指令(以 # 开头的指令)进行处理。
    • #define 定义的符号替换。我们通常使用define定义标识符和宏。假设,我们这里顶一个一个MAX为100,#define MAX 100 ,那么在预编译阶段,我们程序中的MAX会被直接替换成100。;
    • 处理条件编译指令。如 #if、#elif、#else、endif 等;
    • 处理头文件包含指令。如 #include,将被包含的文件插入到该预编译指令的位置;
  2. 删除所有的注释。
  3. 添加行号和文件名标识。

编译

对预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

过程比较复杂,主要包括四个步骤:

  1. 词法分析。这一过程是将字符序列转为单词序列。这里的单词是一个字符串,是构成源代码的最小单位。从输入字符流中生成单词的过程叫作单词化,同时会对单词进行分类。比如:sum = 2+3; 将会拆成sum、=、2、+、3 这些单词。
  2. 语法分析。这一过程是分析单词符号串是否形成符合语法规则的语法单位。如表达式、赋值、循环等,最终是否构成一个符合要求的程序,再按照对应语言的语法规则检查每条语句是否正确。
  3. 语义分析。语义分析是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。比如语义分析的一个工作是进行类型审查,审查每个运算符是否具有语言规范允许的运算对象,当不符合语言规范时,编译程序 应报告错误。
  4. 符号汇总。这个环节会将源文件的全局函数的函数名进行汇总。

汇编

将编译完的汇编代码文件翻译成机器指令,保存在后缀为 .o 的目标文件(Object File)中。同时会给每个源文件汇总出来的符号分配一个地址,然后生成一个符号表。

这个文件是一个 ELF 格式的文件(Executable and Linkable Format,可执行可链接文件格式),包括可以被执行的文件和可以被链接的文件(如目标文件 .o,可执行文件 .exe,共享目标文件 .so),有其固定的格式。

链接

由汇编程序生成的目标文件并不能被立即执行,还需要通过链接器(Linker),将有关的目标文件彼此相连接,使得所有的目标文件成为一个能够被操作系统载入执行的统一整体。

例如在某个源文件的函数中调用了另一个源文件中的函数;或者调用了库文件中的函数等等情况,都需要经过链接才能使用。

在链接阶段,编译器主要完成两个任务:

  1. 合并段表。在汇编结束生成的obj文件,内部其实会被划分为几个段,在链接过程中会将这些段进行合并。
  2. 符合表的合并和重定位。在链接过程中,我们会对不同的符合分配一个相应的地址。而有的时候,一些符号在它所处的文件中并不存在,存在于另一个文件中,这时候我们会提前分配一个无意义的地址。在合并之后,会将合法的地址重新定位覆盖原来无意义的地址。

链接处理可以分为两种:

  • 静态链接:直接在编译阶段就把静态库加入到可执行文件当中去。优点:不用担心目标用户缺少库文件。缺点:最终的可执行文件会较大;且多个应用程序之间无法共享库文件,会造成内存浪费。
  • 动态链接:在链接阶段只加入一些描述信息,等到程序执行时再从系统中把相应的动态库加载到内存中去。优点:可执行文件小;多个应用程序之间可以共享库文件。缺点:需要保证目标用户有相应的库文件。

载入

加载器(Loader)会将可执行文件的代码和数据加载到内存(虚拟内存)中,然后跳转到程序的第一条指令开始执行程序。

虚拟内存

首先,为了避免进程所使用的内存地址相互影响,操作系统会为每个进程分配一套独立的虚拟内存地址,然后再提供一种机制,将虚拟内存地址和物理内存地址进行映射。

我们程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address),实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)。

用户空间

然后,操作系统将整个内存空间分为用户空间和内核空间,其中内核空间只有内核程序能够访问,且所有进程共用一个内核空间;而用户空间是专门给应用程序使用的,每当创建了一个新的进程,都要分配一个用户空间。

接下来以 32 位内存空间为例进行说明,32 位内存空间大小为 4GB,其中 1GB 为内核空间,3GB 为用户空间。用户空间中按照数据类型不同,划分为了不同的内存段,各类数据会被存放到各自的内存段中。

在这里插入图片描述

用户空间内存,从低到高分别是 6 种不同的内存段:

  1. 程序文件段(.text),包括二进制可执行代码;
  2. 已初始化数据段(.data),包括静态常量;
  3. 未初始化数据段(.bss),包括未初始化的静态变量;
  4. 堆段,包括动态分配的内存,从低地址开始向上增长。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);
  5. 文件映射段,包括动态库、共享内存等,从低地址开始向上增长;
  6. 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。当然系统也提供了参数,以便我们自定义大小;栈段可以通过系统调用自动地扩充空间,但是不能回收空间,所以栈段设置得太大会导致内存泄露。

至此,可执行文件载入内存的过程可以概括为以下几步:

  1. 给进程分配虚拟内存空间;
  2. 创建虚拟地址到物理地址的映射,创建页表;
  3. 加载代码段和数据段等数据,即将硬盘中的文件拷贝到物理内存页中,并在页表中写入映射关系;
  4. 把可执行文件的入口地址写入到 CPU 的 指令寄存器(PC)中,即可执行程序。

总结

本文简单的描述了一下一个程序,从编写完代码,到被计算机运行的过程,其实当中的每一步都十分复杂深奥,都值得深入学习,尤其是最后一步载入内存的过程,展开来说可以涉及到整个操作系统的内存管理,像分段、分页、多级页表、TLB、内存分配、内存泄露、内存回收、页面置换算法等等。

【C语言】C语言 4 个编译过程详解 c 编译过程将作为输入的源代码转换为目标代码或机器代码。编译过程可分为预处理、编译、汇编和链接四个步骤。 阅读详情

相关推荐

程序运行过程

一、程序编译过程 gcc HelloWorld.c -E -o HelloWorld.i 预处理:加入头文件,替换宏; gcc HelloWorld.c -S -c HelloWorld.s 编译:包含预处理,将 C 程序转换成汇编程序; gcc HelloWorld.c -c HelloWorld.o 汇编:包含预处理和编译,将汇编程序转换成可链接的二进制程序; gcc HelloWorld.c -o HelloWorld 链接:包含以上所有操作,将可链接的二进制程序和其它别的库链接在一起,.

qq_29044159的博客 616

程序的编译和运行

了解一个C程序是如何从代码到运行的

qq_65285898的博客 3958

[0352]基于JAVA的停车场收费智慧管理系统的设计与实现

其次,从经营管理层面考虑,本系统涵盖了丰富的月卡管理和财务结算功能,如月卡用户管理、缴费、注销、挂失、解挂、补办等全流程服务,以及临保和月保的结算汇总与欠费查询等功能,这不仅有助于规范停车场的日常运营管理,更能有效提高财务管理效率,降低运营成本,实现精细化、数字化的财务管控。综上所述,本课题计划研发的基于Java的停车场收费智慧管理系统将在现有研究成果的基础上,重点优化月卡用户全生命周期管理和财务统计分析功能,以满足现代停车场高效、精准、安全的管理需求,紧跟行业发展趋势,为推动智慧城市的建设贡献一份力量。

muyu2980的专栏 590

详解程序执行过程

详解程序执行过程(基于C语言)

CHAKMING1的博客 1万+

手把手教你调试RTL8201F:从芯片手册解读到LWIP驱动移植

本文详细解析了RTL8201F PHY芯片的开发流程,从寄存器配置到LWIP驱动移植,重点介绍了RMII模式下的特殊配置要点和调试技巧。通过实战案例和高级调试方法,帮助工程师解决网络性能问题,实现与FreeRTOS+LWIP协议栈的稳定集成。

ik678901234的博客 973

ADS学习笔记 2. 低噪声放大器设计

ADS学习笔记 2. 低噪声放大器设计。设计了一个中心频率433MHz,增益18.206dB,NF=0.134dB,S11带宽较好的低噪声放大器

u014087103的博客 9510

【一个程序从编译到运行的全过程】

【一个程序从编译到运行的全过程】

Sugar_wolf的博客 1857

SAP FICO 资产会计AA后台配置 (上)

输入折旧表“CZ11”如有外币计价的资产,则需指定外币计价的资产折旧范围,以及规则。因为在设置资产类型时,需指定一个对应的 资产编号范围、资产账户确定类型(便于分配自动会计科目)、屏幕格式规则(便于设置资产字段必填与否)。因为在设置资产类型时,需指定一个对应的 资产编号范围、资产账户确定类型(便于分配自动会计科目)、屏幕格式规则(便于设置资产字段必填与否)。新增资产类型:Z101 固定资产-房屋及建筑物新增资产类型:Z301 固定资产-房屋及建筑物。

ghgxm520的博客 1762

编译运行第一个Java程序——通过示例学习Java编程3

作者:CHAITANYA SINGH 来源:https://www.koofun.com//pro/kfpostsdetail?kfpostsid=13 在本教程中,我们将了解如何编写、编译和运行Java程序。我还将介绍Java语法、代码约定以及运行java程序的几种方法。 简单Java程序: publicclassFirstJavaProgram{...

weixin_30768661的博客 117

微信小程序 - 最新详细实现调起手机摄像头拍摄身份证人像面和国徽面取景框功能,在拍照相机上增加身份证证件照的轮廓或自定义文字,支持替换扫描拍摄辅助线图片及文本,小程序实现拍照身份证时带取景轮廓框拍摄!

微信小程序,身份证,证件照,人像,证件照提示框模板,vue身份证拍照框,摄像机相机里有引导内容,取景框拍摄,微信小程序实现拍照界面自定义实现,自拍人相框拍摄,取景框,户口簿取景框,画布指定区域生成图片,微信小程序身份证正反面拍照如何加入辅助图片引导照片,身份证取景框拍摄,服务器,上传,正反面,调起手机摄像头: 拍摄身份证的正反面,自定义身份证取景框,在相机上绘制 “身份证轮廓“ 与提示文字,拍摄身份证时出现人像框怎么实现,在相机上增加图片和文字,微信小程序怎么给调起的相机加入自定义图片,小程序实现给相机上面

🏆 前端领域优质创作者 3406

如何用Python打造PubMed API客户端:科研文献检索自动化实践

PubMed提供了官方的Entrez E-utilities API接口,支持程序化检索文献,但直接调用API需处理请求重试、参数封装、数据解析等问题。批量获取文献ID及结构化详情(标题、作者、DOI等)内置重试机制,应对网络波动或API限流统一数据输出格式,便于后续分析(如文献计量、AI文本处理)灵活适配不同检索需求(如指定数据库、结果数量)本文实现的PubMed API客户端,通过封装API请求、重试机制、数据解析,让科研文献检索从“手动点击”变为“代码自动化”。

ASKCOS博客 359

如何在虚拟机中linux系统用gcc编译并运行第一个C++程序

原文:https://blog.youkuaiyun.com/lily_prince/article/details/64906372 首先打开Terminal,一般系统都已安装了gcc,如果未安装就在命令行输入#apt-get install gcc命令,接着输入Y,系统就会自己系统下载gcc哦 输入命令 mkdir my_c++ 在根目录或者其他目录创建一个名叫my_c++的文件夹用来存放程序文...

include_645829461的博客 1万+

SAP-ABAP:SAP库存管理核心增强:IF_EX_MB_DOCUMENT_BADI 深度解析

SAP库存管理核心增强:IF_EX_MB_DOCUMENT_BADI 深度解析

baidu_35680696的博客 1447

编译和运行第一个Java程序——通过示例学习Java编程3

作者:CHAITANYA SINGH 来源:https://www.koofun.com//pro/kfpostsdetail?kfpostsid=13 在本教程中,我们将了解如何编写、编译和运行Java程序。我还将介绍Java语法、代码约定以及运行java程序的几种方法。 简单Java程序: public class FirstJavaProgram { public static void m...

lea41的博客 394

OGRE 3D----2. QGRE + QQuickView

将 OGRE(面向对象图形渲染引擎)集成到使用 QQuickView 的 Qt Quick 应用程序中,可以在现代灵活的 UI 框架中提供强大的 3D 渲染功能。本文将指导您如何在 QQuickView 环境中设置 OGRE。

charce_you的专栏 914

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Java程序编译和运行的过程

 转载自:https://www.cnblogs.com/qiumingcheng/p/5398610.html Java整个编译以及运行的过程相当繁琐,本文通过一个简单的程序来简单的说明整个流程。                  如下图,Java程序从源文件创建到程序运行要经过两大步骤:1、源文件由编译器编译成字节码(ByteCode)  2、字节码由java虚拟机解释运行。因为java程...

Evan's Blog ٩(๑❛ᴗ❛๑)۶ 3293

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

如何使用记事本编译并运行Java源代码

1.在记事本中编辑代码 2.保存记事本中的内容 3.运行程序 ...

weixin_54217081的博客 3万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Centos编译运行一个简单的C++程序

I、准备工作: 1) 一个类UNIX系统,这里选择CentOS 7 2) 安装gcc编译器,方法很多,一种简单的安装方式:yum install gcc-c++   注:yum install g++ 会失败,因为没有g++这个包 安装好后显示如下: 3)创建测试目录 [root@localhost /]# mkdir desk [root@localhost /]# ls desk...

守护大白菜的博客 1万+

1.2 HelloWorld程序编写编译及运行。

1.2 HelloWorld程序编写编译及运行。  创建一个普通文本文件,将其修改为.java文件。  *a: 定义类*  *b: 写main方法  *c: 写输出语句 public class HelloWorld{     public static void main(String[] args){         System.out.println("HelloWo

southdreams的博客 1349

用命令行模式编译运行带包名的java文件

package com; public class Test {  public static void main(String[] args) {   // TODO Auto-generated method stub   System.out.println("可以正常运行了!");  } }

南来北往的客 3196

VS Code的Microsoft C++配置并成功运行一个helloworld程序

Window环境下在VS Code上用Microsoft C++编译器debug和run一个简单的helloworld程序

weixin_44805776的博客 2927

Java程序编译和运行的过程一

    Java整个编译以及运行的过程相当繁琐,本文通过一个简单的程序来简单的说明整个流程。                  如下图,Java程序从源文件创建到程序运行要经过两大步骤:1、源文件由编译器编译成字节码(ByteCode)  2、字节码由java虚拟机解释运行。因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-interpreted...

程序狗的成长之路 463
上一篇: SDL 详解
下一篇: C++ 类型转换
UestcXiye
博客等级 码龄8年 9031粉丝 1306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值