从源代码到可执行程序:四个步骤与详解(代码片段)

fensi fensi     2022-11-28     161

关键词:

"hello world"可以说是所有程序员闭着眼睛都能写出来的代码:

#include <stdio.h>
int main()

    printf("hello world
");
    return 0;  

编译运行一气呵成。而每当有人问起:从源码到可执行程序有哪些步骤,大多数程序员面对这个问题也能脱口而出:预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)

不过很多人不了解其中都做了哪些处理,今天就带大家来好好聊一聊。

预编译

处理的第一步,是将源码文件.c和头文件.h编译成一个.i文件。

预编译过程主要是做了以下一些工作:

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

处理条件编译指令,比如#ifdef、#ifndef、#if、#endif等等

处理#include指令,将头文件插入到该指令的位置。这个过程是递归的,也就是说被包含的头文件还可能包含其他头文件

删除所有注释

添加行号和文件名标识,比如# 2 "main2.c" 2 ,以便于编译产生错误或者警告的时候能够显示行号以及编译器产生调试用的行号信息

保留所有的#pragma编译器指令,因为编译器要使用它们。#pragma的作用是设定编译器的状态或者是指示编译器完成一些特定的动作

编译

编译过程就是将预处理完的文件进行词法分析语法分析语义分析优化后产生相应的汇编代码文件

词法分析

词法分析主要使用词法分析器(也叫扫描器),将源代码的字符序列分割成一系列的符号(Token)。比如如下一段程序:

int array = (index + 4) * 2;

经过扫描以后,产生11个记号:

int         关键字

array      标识符

=         赋值操作符

(        左小括号

index      标识符

+        加号

4        数字

)        右小括号

*        乘号

2        数字

;        语句结束

语法分析产生的记号一般可以分为:关键字,标识符,字面量(包括数字和字符串等)和特殊符号(加号减号等)。在识别记号的同时,扫描器也完成其他工作,比如讲标识符存放到符号表,讲数字字符串常量存放到文字表,以备后面的步骤使用。

语法分析

接下来语法分析器将对由扫描器产生的记号进行语法分析,从而产生语法树。整个分析过程采用了上下文无关语法的分析手段。

语义分析

这个阶段由语义分析器来完成。语法分析仅仅完成了对表达式的语法层面的分析,他并不了解这个语句是不是真的有意义。比如两个指针相乘是没有意义的,但是在语法上是合法的。

编译器可以分析的语义是静态语义,即在编译器就可以确定的语义;与之对应的是动态语义,即在运行期才可以确定的语义。

静态语义通常包括声明和类型的匹配,类型的转换。比如一个浮点型表达式赋值给整形表达式的时候,语义分析会完成浮点型到整形的转换。动态语义使之运行期出现的语义相关问题,比如除数是0的时候会报运行期语义错误。

源代码优化

现代编译器有很多层的优化,往往在源代码级别会有一个优化过程。源代码优化器会在源码级别进行优化,比如一行代码:

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

在这行代码中,(2+6)这个表达式就可以被优化掉,因为他的值在编译器就可以确定。

在进行了语法分析和语义分析阶段的工作之后,有的编译程序将源程序变成一种内部表示形式,这种内部表示形式叫做中间语言或中间表示或中间代码。所谓“中间代码”是一种结构简单、含义明确的记号系统,这种记号系统复杂性介于源程序语言和机器语言之间,容易将它翻译成目标代码。

中间代码使得编译器可以分为前端和后端,前端负责产生机器无关的中间代码,编译器后端将中间代码转换成目标机器代码。这样对于一些跨平台的编程语言,他们可以针对不同平台使用同一个前端和针对不同平台的数个后端。

目标代码生成与优化

源码级优化器产生中间代码标志着下面的过程都属于编辑器的后端。编译器后端主要包括代码生成器和目标代码优化器。代码生成器将中间代码转换成目标机器代码,然后目标代码优化器进行代码优化,比如选择合适的寻址方式、食用为宜来代替乘法运算,删除多余的指令。

 

经过扫描、词法分析、语法分析、语义分析、源代码优化、代码生成和目标代码优化,源代码终于被编译成了目标代码。但是现在还有一个问题:目标代码中有的变量定义在其他模块,我们该怎么办?

事实上,定义在其他模块的变量和函数在最终运行时的绝对地址都要在链接的时候才能确定。所以现代编译器可以将一个源代码文件编译成一个未链接的目标文件,然后由链接器最终将这些目标文件链接起来形成可执行文件。

链接

暂时挖个坑

从源代码到可执行文件(代码片段)

请简述程序从源代码到可执行程序的过程,面试经常会被问到!这是一个屡见不鲜的问题,总而言之就如下四个过程:预处理比如有test.c源文件,内容如下:#include<stdio.h>/*主函数*/intmain(void) printf("h... 查看详情

源代码到可执行程序的过程详解:预编译编译汇编链接(代码片段)

1、gcc编译过程分解(1)首先是将.c源文件和.h头文件经过预编译(cpp是预编译器),得到.i文件,主要是进行的一些替换工作;(2)将.i文件经过编译器(gcc)处理,得到.s汇编文件,现在文件内容已经从C语言编程了汇编... 查看详情

从源代码到可执行文件(代码片段)

请简述程序从源代码到可执行程序的过程,面试经常会被问到!这是一个屡见不鲜的问题,总而言之就如下四个过程:预处理比如有test.c源文件,内容如下:#include<stdio.h>/*主函数*/intmain(void) printf("h... 查看详情

一个c++源文件从文本到可执行文件经历的过程(代码片段)

一个C++源文件从文本到可执行文件经历的过程以HelloWorld为例进行说明首先我们编写一个cpp源程序test.cpp#include<iostream>usingnamespacestd;intmain()cout<<"helloworld"<<endl;return0;使用g++编译命令时g++-otesttest.cppGcc编 查看详情

从程序到进程

...p://www.civilnet.cn/bbs/browse.php?topicno=78426 本文以《从代码到可执行文件》为基础,阅读本文前确保你熟悉了《从代码到可执行文件》中提到的概念,本文中的示例程序仍是《从代码到可执行文件》中的gemfield.c。代码如下:********... 查看详情

网络安全-反弹shell详解(攻击,检测与防御)(代码片段)

...求。原理操作受害者机器,将某开放端口的数据发送到可执行命令的程序上,将结果返回给攻 查看详情

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

(1)预编译主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下:删除所有的#define,展开所有的宏定义。处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。处... 查看详情

计算机系统-概述(代码片段)

...的唯一方法:根据字节序列的上下文2.编译过程从源文件到可执行目标文件,执行:gcc-ohellohello.c分为四个阶段:预处理阶段:预处理器(cpp)修改原来的C程序,得到hello.i文件编译阶段:编译器(ccl)将hello.i文件翻译成hello.s文... 查看详情

jvm执行引擎篇(解释器与编译器)(代码片段)

...器详解5.执行方式设置1.编译和执行的过程大部分的程序源代码转换成物理机的目标代码或虚拟机能执行的指令集之前,都需要经历如下步骤代码编译由Java源码编译器javac完成代码执行由Java虚拟机执行引擎完成2.编译器和解释... 查看详情

线程同步与互斥详解(代码片段)

...斥进程线程间的互斥相关背景概念临界资源:多线程执行流共享的资源就叫做临界资源临界区:每 查看详情

lampphp详解(代码片段)

...:静态资源:原始形式与响应内容一致,在客户端浏览器执行动态资源:原始形式通常为程序文件,需要在服务器端执行之后,将执行结果返回给客户端Web相关语言客户端技术:html,javascript服务器端技术:php,jsp,python,asp通常... 查看详情

使用 CMake 将资源(例如,着色器代码;图像)嵌入到可执行文件/库中

】使用CMake将资源(例如,着色器代码;图像)嵌入到可执行文件/库中【英文标题】:Embedresources(eg,shadercode;images)intoexecutable/librarywithCMake【发布时间】:2012-08-0212:54:50【问题描述】:我正在用C++编写一个应用程序,它依赖于我... 查看详情

6c_宏定义与预处理函数与函数库(代码片段)

C语言预处理理论由源码到可执行程序的过程源码.c->(编译)->elf可执行程序源码.c->(编译)->目标文件.o->(链接)->elf可执行程序源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序源码.c->(... 查看详情

线程详解(代码片段)

...乐播放器播放一首歌,音乐播放器就是一个进程,在程序执行时,既有声音的输出,同时还有该歌曲的字幕展示,这就是进程中的两个线程线程与进程程序进入内存就变成了进程,进程就是处于运行中的程序进程特征:独立性:... 查看详情

dockerfile详解与镜像发布(代码片段)

...志指向文件系统中任何位置的Dockerfile。Docker镜像发布的步骤:1、编写一个dockerfile文件2、dockerbuild构建成为一个镜像3 查看详情

分析详解python多线程与多进程区别(代码片段)

...C都是多核的,使用多线程能充分利用CPU来提供程序的执行效率。1.1线程线程是一个基本的CPU执行单元。它必须依托于进程存活。一个线程是一个executioncontext(执行上下文),即一个CPU执行时所需要的一串指令。1.... 查看详情

rrt与rrt*算法具体步骤与程序详解(python)(代码片段)

...、RRT*算法编写的步骤1.算法的步骤2.算法的实现三、所有程序附录RRT算法RRT*算法前言RRT和RRT*的区别:RRT的中文名为快速随机探索树,它的原理很简单,实际上就是维护一棵路径树:从起点开始,在空间中随机... 查看详情

#从源码到可执行程序

从源码到可执行程序从源码到可执行程序1.概述2.预编译3.编译4.汇编5.链接1.概述  很多编译型语言的源码都需要被编译之后才可以正常的运行,如常用的c语言,它需通过编辑器的编译把它变成0101序列的机器语言才可以在... 查看详情