C语言程序的翻译环境和执行环境

简介: C语言程序的翻译环境和执行环境

一、概述:翻译环境、执行环境

在ANSI C(标准C语言)的任何一种实现中,都存在两种环境:

1.翻译环境

在这个环境中,源代码被转换为可执行的二进制指令

在一个项目工程中,test.c就是源文件(源代码),可以在存放该项目工程的文件夹中找到该源文件。该源文件存放的是文本信息代码,可以直接用记事本打开,可以看到源代码

这样的源代码并不能直接运行,而是要通过翻译环境形成一个可执行程序text.exe,该可执行程序中存放的是二进制指令(机器指令)。同样也可以用记事本打开,但是打开后看到的是一堆乱码,因为存放的都是二进制的信息,记事本打开无法查看。

2.执行环境(运行环境)

用于实际执行代码(执行二进制指令)

当源文件test.c通过翻译环境产生可执行文件test.exe后,该可执行文件就可以通过执行环境执行,最终得到执行结果。

二、详述翻译环境——编译环境、链接环境

在翻译环境下,又分为两个环境:编译环境和链接环境

1.编译环境

源文件test.c通过编译器生成目标文件test.obj(Linux环境下的目标文件时test.o),该目标文件中存放的是二进制指令,用记事本打开同样是一堆乱码。

(VS2022环境下的编译器是cl.exe)

如果有多个源文件,那么这些源文件都要单独通过编译器生成对应的目标文件

2.链接环境

所有目标文件和链接库经过链接器的处理最终生成一个可执行文件test.exe

(VS2022环境下的链接器是link.exe)

链接库:是Windows系统中封装代码和数据以及实现资源共享的一种方式,本质上是已经编译好的二进制指令文件(机器指令文件)

例如包含头文件时使用的C语言标准库就是以链接库的形式和其他目标文件通过链接器链接

三、详述编译过程——预编译、编译、汇编

编译器对源文件进行编译的具体过程分为3步:预编译(预处理)、编译、汇编

1.预编译(预处理)

预编译时,编译器会进行以下动作:

1.将所有的注释替换为空格

2.将#define定义的标识符(宏)替换为对应的常量或函数(宏展开)

3.将包含的头文件中所有的内容以及源文件代码整合并生成一个test.i文件

(预处理后得到的test.i文件仍然是C语言代码)

VS2022环境下,将编译器设置改为预处理到文件,这样编译器会将预处理完成后的结果放到test.i文件。相比于源文件,该test.i文件末尾代码是进行了注释替换和宏展开的源代码,上面还有一万多行代码是将头文件所包含的所有内容。

2.编译(Linux环境下测试)

将test.i文件的C语言代码翻译成汇编代码,存放到test.s文件中

编译器如何将C语言代码翻译成汇编代码?
(1)词法分析

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

例如下面代码,代码中的字符可被分割成一系列的记号:

(2)语法分析

语法分析器对扫描器扫描产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点的树。

(3)语义分析

主要是检查是否结构正确的句子所表示的意思也合法、执行规定的语义动作(如:表达式求值、符号表填写、中间代码生成等)

(4)符号汇总

将所有源文件的全局符号汇总起来,包括全局变量名、全局函数名、main。(因为只有全局的函数、变量才涉及到跨文件使用)

3.汇编(Linux环境下测试)

将test.s汇编代码通过汇编器翻译为二进制指令,存放到test.o目标文件中(Windows环境下的目标文件为test.obj)

汇编器如何将汇编代码翻译成二进制指令?
(1)生成符号表

每个源文件都有自己的符号,汇编器会将源文件各自的符号列成一个表,并为每个符号给予地址。

例如,在下列项目中,有两个源文件 add.c 和 test.c ,汇编器在各自目标文件(.obj)中文件中生成符号表

add.obj

test.obj

四、详述链接过程——合并段表、符号表的合并与重定位

1.合并段表

在gcc编译器(Linux环境)中,所有生成的目标文件和二进制文件都是按照 elf 文件格式组织的。将所有的目标文件或二进制文件分成不同的段,每个段存放不同的数据。

合并段表就是将这些目标文件和二进制文件相同的段进行合并,生成二进制可执行文件

2.符号表的合并与重定位

每个目标文件都有自己的符号表,需要将这些符号表进行合并

最终去查找函数的时候,只需要通过这个合并的符号表根据各符号的地址去查找即可

正是因为符号表合并的存在,才可以进行函数等的跨文件调用。

如果程序运行时出现了未定义的外部符号报错,说明源文件中并未定义该符号。根据地址查找该符号时,找不到该符号的定义。

五、详述运行环境——程序的执行过程

程序的执行过程
1.程序载入内存中

在有操作系统的环境中,该操作由操作系统完成。在独立的环境中,该操作由手工完成,或者通过可执行代码置入只读内存来完成。

2.调用main函数

程序载入内存完成后,紧接着便调用main函数

 

3.执行程序代码

此时程序调用一个运行时堆栈,存储函数的局部变量和返回地址。同时也调用一个静态内存,存储静态变量和全局变量,存储于静态内存中的变量在程序的整个执行过程中一直保留它们的值。

 

4.终止程序

正常终止main函数,或发生意外终止main函数

目录
相关文章
|
2月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
4月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
106 1
|
4月前
|
NoSQL 编译器 程序员
【C语言】揭秘GCC:从平凡到卓越的编译艺术,一场代码与效率的激情碰撞,探索那些不为人知的秘密武器,让你的程序瞬间提速百倍!
【8月更文挑战第20天】GCC,GNU Compiler Collection,是GNU项目中的开源编译器集合,支持C、C++等多种语言。作为C语言程序员的重要工具,GCC具备跨平台性、高度可配置性及丰富的优化选项等特点。通过简单示例,如编译“Hello, GCC!”程序 (`gcc -o hello hello.c`),展示了GCC的基础用法及不同优化级别(`-O0`, `-O1`, `-O3`)对性能的影响。GCC还支持生成调试信息(`-g`),便于使用GDB等工具进行调试。尽管有如Microsoft Visual C++、Clang等竞品,GCC仍因其灵活性和强大的功能被广泛采用。
132 1
|
4月前
|
编译器 C语言 计算机视觉
C语言实现的图像处理程序
C语言实现的图像处理程序
166 0
|
2月前
|
存储 文件存储 C语言
深入C语言:文件操作实现局外影响程序
深入C语言:文件操作实现局外影响程序
|
3月前
|
存储 编译器 程序员
C语言程序的基本结构
C语言程序的基本结构包括:1)预处理指令,如 `#include` 和 `#define`;2)主函数 `main()`,程序从这里开始执行;3)函数声明与定义,执行特定任务的代码块;4)变量声明与初始化,用于存储数据;5)语句和表达式,构成程序基本执行单位;6)注释,解释代码功能。示例代码展示了这些组成部分的应用。
100 10
|
2月前
|
Linux C语言 iOS开发
MacOS环境-手写操作系统-06-在mac下通过交叉编译:C语言结合汇编
MacOS环境-手写操作系统-06-在mac下通过交叉编译:C语言结合汇编
25 0
|
3月前
|
Shell Linux API
C语言在linux环境下执行终端命令
本文介绍了在Linux环境下使用C语言执行终端命令的方法。首先,文章描述了`system()`函数,其可以直接执行shell命令并返回结果。接着介绍了更强大的`popen()`函数,它允许程序与命令行命令交互,并详细说明了如何使用此函数及其配套的`pclose()`函数。此外,还讲解了`fork()`和`exec`系列函数,前者创建新进程,后者替换当前进程执行文件。最后,对比了`system()`与`exec`系列函数的区别,并针对不同场景推荐了合适的函数选择。
|
4月前
|
自然语言处理 编译器 C语言
C语言程序的编译
C语言程序的编译
69 2
|
5月前
|
Linux C语言 Windows
C语言文件编程-Linux环境下运行
本文介绍了在Linux环境下使用C语言进行文件编程时的两种主要接口:C标准库函数与Linux系统调用。C标准库提供了`fopen`, `fread`, `fwrite`, 和 `fclose`等函数,适用于普通文件操作;而Linux系统调用如`open`, `read`, `write`, 和 `close`则更适合处理设备文件,同时也可用于普通文件。这两种方法的主要区别在于前者使用文件指针,后者使用文件描述符。文章还给出了两个示例程序:一个使用C标准库函数实现文件复制,另一个则使用Linux系统调用完成相同任务。