[ 编程语言 ] C : 0x01_程序

  • 一、简要介绍
  • 二、文本编辑
  • 三、编译详解
    • Step:1,预处理阶段(Pre-processing)
    • Step:2,编译阶段(Compiling)
    • Step:3,汇编阶段(Assembling)
    • Step:4,链接阶段(Linking)
  • 四、目标生成
  • 五、资料

一、简要介绍


我们要知道,计算机是一个机器,主要由处理器,内存,硬盘和输入输出设备组成,是可以用来解决问题的通用工具,然而,人不是机器,也不可能也没必要成为机器。但计算机又不能理解人类思考问题的方法。

为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路,方法和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完成某种特定的任务。

这时候,程序设计语言被创造出来了,创造程序设计语言的目的是为程序员服务的,是人与计算机进行交流的桥梁,十分接近人们在日常生活中使用的语言和思维习惯,建立统一标准,直观易懂,便于学习。

程序,即为实现特定目标或解决特定问题而用计算机语言(能被人读懂的,程序设计语言)编写的命令序列的集合,基本上就是告诉计算机要操作的数据和执行的命令序列,即对什么数据做什么操作。

可以说,程序,是一组计算机能识别和执行的指令,控制着计算机各部件,运行于计算机上,用于满足人们某种需求的信息化工具。

所有的计算机程序一般分为两大类:

  • 系统程序(系统软件)
  • 应用程序(应用软件)

但本质上都逃不开以下范畴:

  • 数据的描述;(数据结构)
  • 操作的描述。(算法)

程序能否快速而有效地完成预定的任务,取决于是否选对了数据结构,程序是否能清楚而正确地把问题解决,则取决于算法。

一般的,程序通过程序设计语言(如C语言)编写实现,然后在编译的过程中,被编译器/解释器转译,成可执行目标程序才能可以由操作系统进行加载到内存中执行。

二、文本编辑


源文件是指纯文本,内部没有特殊格式的文件,但对于计算机语言来说,其源文件的命名都会有一个特定的后缀,以方便编译器识别,程序员区分,例如:

计算机语言(编程语言) 后缀名
C .c
C++ .cpp
Python .py
Java .java
选用几款好用的文本编辑器,可以让编码更方便快捷有效率
  • Vim
  • Sublime Text
  • Visual Studio Code
  • UltraEdit
  • GNU Emacs

使用文本编辑器进行代码编辑时需要注意以下几个问题

  • 文件编码需注意;
  • 严格区分中英文;
  • 严格区分全半角。

三、编译详解


源文件中的代码是由固定的词汇按照固定的格式组织起来的,简单直观,程序员容易识别和理解。
但是,对于Cpu而言,识别由一系列计算机指令和数据的集合组成的程序,是二进制形式的,两者不相通的。
这时候就需要一个特殊软件工具,读取源文件的代码词汇、句子以及各种特定的格式并将它翻译成一个Cpu能够识别的可执行目标程序,这个特殊的软件工具,叫编译器驱动程序,而这个过程被称为编译(翻译)。

C语言中编译器是有很多种的,不同的平台下也有不同的编译器,例如:

  • (Windows,MinGW)
  • (Linux,Gcc)
  • (macOS,LLVM/Clang)

Gcc的编译过程为例子,Gcc命令的一般格式为:

gcc [选项] [源文件] [选项] [目标文件]

其中,选项和目标文件可缺省,Gcc默认生成可执行文件,命名为:a.out

参数 说明
-E 使Gcc编译器预处理完毕即刻停止
-S 使Gcc编译器在执行完编译后停止,生成汇编程序
-c 使Gcc编译器在执行完汇编后停止,生成目标文件
-o 使用此选项时,可指定生成可执行文件名,如没有添加其他选项,则Gcc一步到位执行到链接后停止,生成最终的可执行文件(缺省选项时,默认可执行文件名为a.out

我们可以使用Gcc一步到位编译:

localhost@linux:~$ gcc main.c -o main

使用GccC语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤(预处理器,编译器,汇编器,连接器)一起构成了编译系统

#mermaid-svg-4pC78f4fBU39wKwA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4pC78f4fBU39wKwA .error-icon{fill:#552222;}#mermaid-svg-4pC78f4fBU39wKwA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4pC78f4fBU39wKwA .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-4pC78f4fBU39wKwA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4pC78f4fBU39wKwA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4pC78f4fBU39wKwA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4pC78f4fBU39wKwA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4pC78f4fBU39wKwA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4pC78f4fBU39wKwA .marker.cross{stroke:#333333;}#mermaid-svg-4pC78f4fBU39wKwA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4pC78f4fBU39wKwA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4pC78f4fBU39wKwA .cluster-label text{fill:#333;}#mermaid-svg-4pC78f4fBU39wKwA .cluster-label span{color:#333;}#mermaid-svg-4pC78f4fBU39wKwA .label text,#mermaid-svg-4pC78f4fBU39wKwA span{fill:#333;color:#333;}#mermaid-svg-4pC78f4fBU39wKwA .node rect,#mermaid-svg-4pC78f4fBU39wKwA .node circle,#mermaid-svg-4pC78f4fBU39wKwA .node ellipse,#mermaid-svg-4pC78f4fBU39wKwA .node polygon,#mermaid-svg-4pC78f4fBU39wKwA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4pC78f4fBU39wKwA .node .label{text-align:center;}#mermaid-svg-4pC78f4fBU39wKwA .node.clickable{cursor:pointer;}#mermaid-svg-4pC78f4fBU39wKwA .arrowheadPath{fill:#333333;}#mermaid-svg-4pC78f4fBU39wKwA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4pC78f4fBU39wKwA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4pC78f4fBU39wKwA .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-4pC78f4fBU39wKwA .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-4pC78f4fBU39wKwA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4pC78f4fBU39wKwA .cluster text{fill:#333;}#mermaid-svg-4pC78f4fBU39wKwA .cluster span{color:#333;}#mermaid-svg-4pC78f4fBU39wKwA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4pC78f4fBU39wKwA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

编译系统
源码 : Source code
预处理 : Pre-processing
编译 : Compiling
汇编 : Assembling
链接 : Linking
可执行文件 : Executable

Step:1,预处理阶段(Pre-processing)


预处理,又称为预编译,即生成预编译文件(预编译,.i)。预处理器读出源代码,对其中内嵌的指示字进行响应(可以说预处理的行为是由指示字/预处理指令控制的),产生源代码的修改版本。修改后的版本会被编译器读入,使用编译器会自动运行预处理器,对程序进行编译预处理。
源代码中的预处理指令叫做指示字(directive) ,从源代码中可以轻易发现,它们以井号#开始,在每行都是第一个非空字符,而井号通常都在第一列,后面紧跟着指示字的关键字。其主要处理规则如下:

  • 处理所有#include指令 ,将被包含的文件插入到该预处理指令的位置;(这个过程是递归的,也就是说被包含的文件还可能包含其他文件)
  • 将所有的#define指令删除,并展开所有的宏定义,实现文本替换;
  • 处理所有条件预处理指令,比如#if#ifdef#elif#else#endif
  • 保留所有#pragma指令,因为编译器需要使用它们;(设定编译器的状态或者是指示编译器完成一些特定的动作)
  • 添加行号和文件标识;(编译时检查到错误或警告的行号提示与调试时查找性能瓶颈和跟踪软件bug的所用的行号提示都是出自这里)
  • 所有的注释删除。(///* */

使用Gcc编译器预处理操作的命令如下:

localhost@linux:~$ gcc -E main.c -o main.i

Step:2,编译阶段(Compiling)


编译,指的是使用编译器将源程序翻译成为目标程序,即转换预编译文件为汇编文件(翻译,.S),但不是指程序从源程序到可执行程序的全部过程。
编译器所要完成的工作是一个复杂的整体的过程,整个工作过程是划分成步骤进行的,每个步骤将源程序的一种表示形式转换成另外一种表示形式,各个阶段进行的操作在逻辑上是紧密连接在一起的,符号表管理器和出错处理贯穿编译器工作的各个步骤,典型的划分方法主要分为以下几个步骤:

#mermaid-svg-bSMOf9n12lH9RJl1 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .error-icon{fill:#552222;}#mermaid-svg-bSMOf9n12lH9RJl1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bSMOf9n12lH9RJl1 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-bSMOf9n12lH9RJl1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bSMOf9n12lH9RJl1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bSMOf9n12lH9RJl1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bSMOf9n12lH9RJl1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bSMOf9n12lH9RJl1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bSMOf9n12lH9RJl1 .marker.cross{stroke:#333333;}#mermaid-svg-bSMOf9n12lH9RJl1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bSMOf9n12lH9RJl1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .cluster-label text{fill:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .cluster-label span{color:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .label text,#mermaid-svg-bSMOf9n12lH9RJl1 span{fill:#333;color:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .node rect,#mermaid-svg-bSMOf9n12lH9RJl1 .node circle,#mermaid-svg-bSMOf9n12lH9RJl1 .node ellipse,#mermaid-svg-bSMOf9n12lH9RJl1 .node polygon,#mermaid-svg-bSMOf9n12lH9RJl1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bSMOf9n12lH9RJl1 .node .label{text-align:center;}#mermaid-svg-bSMOf9n12lH9RJl1 .node.clickable{cursor:pointer;}#mermaid-svg-bSMOf9n12lH9RJl1 .arrowheadPath{fill:#333333;}#mermaid-svg-bSMOf9n12lH9RJl1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bSMOf9n12lH9RJl1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bSMOf9n12lH9RJl1 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-bSMOf9n12lH9RJl1 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-bSMOf9n12lH9RJl1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bSMOf9n12lH9RJl1 .cluster text{fill:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 .cluster span{color:#333;}#mermaid-svg-bSMOf9n12lH9RJl1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bSMOf9n12lH9RJl1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

COMPILE
字符流
符号流
语法树
语法树
中间表现形式
目标机器语言
目标机器语言
目标程序
源程序
词法分析
语法分析
语义分析
中间代码生成
代码优化
目标代码生成
符号表管理
错误处理

使用Gcc编译器编译操作如下:

localhost@linux:~$ gcc -S main.i -o main.S

Step:3,汇编阶段(Assembling)


汇编,指的是使用汇编器将源程序翻译成为目标程序,即翻译汇编文件为二进制文件(翻译,.o)。
使用Gcc编译器汇编操作如下:

localhost@linux:~$ gcc -c main.S -o main.o

此期间进行了汇编指令的翻译,符号表,重定位表的生成,没有生成最终目标可执行程序的,而是生成的是可链接的二进制文件。

高级语言、汇编语言、机器语言是分别对应计算机系统的不同抽象层级。
早期,没有高级语言和汇编语言的时候,程序员靠查询机器语言手册,把想要计算机完成的工作的一系列指令翻译成机器语言并提供给计算机,这个过程是及其繁琐的。
后来,人们发明了助记符,把机器语言抽象成一系列直观的,比较容易记住的汇编指令,一条汇编指令可以完成诸如内存读写、算术逻辑运算、栈维护、过程调用和返回等等操作,并通过汇编器将汇编指令翻译成机器指令。
再后来,人们觉得汇编指令抽象程度还不够高,于是发明了高级语言。

编译器产生汇编而不是适当的机器代码的其他原因是:

  • 汇编程序使用的符号地址而不是硬编码的机器地址,使代码重定位更加容易。
  • 链接代码可能涉及安全检查,例如类型检查,而使用符号名称则更容易。
  • 通过更改汇编器而不是代码生成器,可以更轻松地适应机器代码中的微小更改。

Step:4,链接阶段(Linking)


链接,是一个打包的过程,指的是使用链接器(Linker)将所有二进制形式的目标文件和系统组件组合成一个可执行文件,处理的是可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址),这个文件可被加载(复制)到内存并执行。
而链接器的主要作用是把各个模块之间相互引用(全局符号的互相引用,链接器往往会忽视局部符号)的部分处理好, 使得各个模块之间能够正确的衔接,把一些指令对其他符号地址的引用加以修正。
使用Gcc编译器链接操作如下:

localhost@linux:~$ gcc main.o -o main

在目标文件中,各个段没有具体的起始地址,只有段大小信息,各个标识符没有实际地址,只有段中的相对地址,段和标识符的实际地址需要链接器具体确定,把可重定位文件依次读入,分析各个文件的文件头,进而依次读入各个文件的段,并计算各个段的虚拟内存位置,把每一个符号定义与一个内存位置的符号引用相关联(符号解析),从而重定位这些段并合并,然后修改所有对这些符号的引用,使得他们指向这个内存位置(确定最终地址)。
在编译阶段生成目标文件时,会暂时搁置那些外部引用,而这些外部引用就是在链接时进行确定的,链接器在链接时,会根据符号名称去相应模块中寻找对应符号。待符号确定之后,链接器会重写之前那些未确定的符号的地址,这个过程就是重定位。链接一般分为静态链接、载入时动态链接以及运行时动态链接三种。

四、目标生成


对于整个编译系统而言,处理的每一个源程序,都将最终都会得到相应的目标文件。目标文件中所存放的是与源程序等效的目标的机器语言代码。在Linux平台中有着特有的文件格式,ELF格式,是一种对象文件的格式。我们可以使用readelf命令查看其构成,由段组成,通常一个目标文件中至少有两个段:

  • 代码段

    • 包含的主要是程序的指令。
    • 可读和可执行的,但不可写。
  • 数据段
    • 主要存放程序中要用到的各种全局变量或静态的数据;
    • 可读,可写,可执行。

ELF格式,常被称为可执行和可链接格式 (Executable and Linkable FormatELF),是可执行文件、目标代码、共享库和核心转储的通用标准文件格式。UNIX环境下主要有三种类型的目标文件:

  • 可执行目标文件(Executable File,由于它已经全部完成了重定位工作,可以由操作系统进行加载到内存中执行的文件)
  • 可重定位目标文件(Relocatable File,用于和目标文件链接并生成可执行目标文件或者共享库的二进制代码和数据)
  • 共享目标文件(Shared Object File,一种特殊的可重定位目标文件,可以在加载或者运行时被动态地加载进内存并链接)

那么,可能有的朋友就会想到既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢?
因为编译只是将写的代码转换成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。目标文件经过链接(Link)以后才能变成可执行文件。

编译器和汇编器生成可重定位文件(包括共享文件),连接器生成可执行文件。我们在最后是要运行生成的可执行文件的,如不是当前用户和所属组生成的,则是要赋予权限,才能执行

localhost@linux:~$ chmod a+x main

一般运行可执行文件有两种方式:

  1. 绝对路径(~:家目录,这里我用${HOME}代替,如不确定当前位置,则可以使用pwd查看)
localhost@linux:~$ ${HOME}/main
  1. 相对路径(以当前位置为准,这里我用.代替)
localhost@linux:~$ ./main

五、资料

ELF_Format
man_elf

[ 编程语言 ] C : 0x01_程序相关推荐

  1. python编程语言汇总-最全的编程语言汇总,程序员你可要存好了!

    原标题:最全的编程语言汇总,程序员你可要存好了! 编程语言(programming language),是用来定义计算机程序的形式语言.它是一种被标准化的交流技巧,用来向计算机发出指令.一种计算机语言 ...

  2. 最全的编程语言汇总,程序员你可要存好了!

    编程语言(programming language),是用来定义计算机程序的形式语言.它是一种被标准化的交流技巧,用来向计算机发出指令.一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精 ...

  3. 焦虑的 BAT、不安的编程语言,揭秘程序员技术圈生存现状!

    [编者按]在迭代不休的技术圈中,仅在过去的一个月期间,我们见证了有史以来第一张黑洞照片的诞生:经历了为让人义愤填膺的 996:思考了作为程序员的年龄之槛:膜拜了技术大神的成长历程:追逐了如编程语言.人 ...

  4. python编程语言的优缺点-程序员千万不要入错行!常见的AI编程语言优缺点比较...

    人工智能编程是一种技术的提升,为不同公司的运营和人们的生活带来了极高的效率和最佳效益.人工智能为不同的行业带来了另一种智能技术,其潜力的前景仍在增长,期望它能够达到人类的智慧. 这是因为开发人员愿意探 ...

  5. 阿里、百度、华为都用什么编程语言?做程序员前,这些别说不知道!

    学习编程语言之前,首先要搞清楚"编程语言"这个概念. 很小的时候,父母就教我们开口说话,也教我们如何理解别人讲话的意思.经过长时间的熏陶和自我学习,我们竟然在不知不觉中学会了说话, ...

  6. 如何系统学习一门编程语言? | 黑马程序员

    一.从认识编程语言开始: 语言有很多种,包括汉语.英语.法语.韩语等,尽管输出的形式不同,但可以达到同样的目的.同样,我们也可以通过「语言」来控制计算机,让计算机为我们做事情,这样的语言就叫做编程语言 ...

  7. mysql是一门编程语言吗_掌握一门编程语言对非程序员的工作和生活有多大好处?...

    编程的作用之一,就是可以减少重复性的劳动,所以非程序员如果能够掌握一门编程语言的话,可以大幅度地提高工作效率和质量:甚至有些时候不需要掌握编程语言,只要懂一些程序员常用的软件,就可以让你的工作事半功倍 ...

  8. HTML5定稿了,终于有一种编程语言开发的程序可以在Android和IOS两种设备上运行了

    过去这些年,HTML5 颠覆了 PC 互联网的格局,优化了移动互联网的体验,接下来,HTML5 将颠覆原生 App 世界.这听起来有点危言耸听,但若认真分析 HTML5 的发展史,你会发现,这个世界的 ...

  9. 索骥馆-编程语言之《程序语言的奥妙:算法解读(四色全彩)》扫描版[PDF]

    内容简介: 在我们生活的世界中,各种各样形形色色的事物和现象,其中都必定包含着科学的成分.在这些成分中,有些是你所熟知的,有些是你未知的,有些是你还一知半解的.面对未知的世界,好奇的你是不是有很多疑惑 ...

最新文章

  1. 2020ICPC·小米 网络选拔赛第一场(D. Router Mesh)
  2. Technet:服务器虚拟化备份和灾难恢复
  3. 收藏长文|Java 代码精简之道
  4. Gradle 使用技巧(一)
  5. sqlserver date转nvarchar_数据库干货:整理SQLServer非常实用的脚本
  6. php 文件写入磁盘错误,Linux磁盘读写故障的通常处理流程
  7. linux 添加编程环境变量配置
  8. 复试分数线该怎么划定呢(洛谷P1068题题解,Java语言描述)
  9. 基于Lumisoft.NET组件开发碰到乱码等一些问题的解决
  10. ssis 列转换_SSIS组播转换概述
  11. ExtJs6获取form里的数据
  12. IT行业里有这么多聪明人,他们之间的区别在哪里?
  13. tensorflow中的global_step参数(转)
  14. 在中国搞技术只能混碗饭吃,没有太大希望
  15. MySQL命令行乱码问题的解决
  16. java自定义对象集合排序
  17. Reveal Effect(揭露效果)低版本兼容
  18. java根据经纬度获取详细地址
  19. python中forward是什么意思_pytorch 调用forward 的具体流程
  20. Seraph‘s Last Stand(塞拉弗的最后一站)技能翻译

热门文章

  1. 罗昭锋 文献管理与信息分析二
  2. 【DIY】使用STM32及PID算法实现一个磁悬浮玩具
  3. Freeline - Android平台上的秒级编译方案
  4. 有哪些好的java入门刷题网站_计算机学习中有哪些比较好的刷题网站
  5. 什么是CMS?企业开发使用什么CMS?
  6. 爱数oracle rac备份,客户案例 |爱数AnyStorage打造宁乡县人民医院RAC+虚拟化混合架构容灾...
  7. 小程序之上传多张图片
  8. 卖完电视卖电脑,百年东芝是怎么没落的?
  9. 解析python爱心代码(码农高天版)
  10. 龟虽寿 [魏晋] 曹操