精华内容
下载资源
问答
  • 编译原理类型检查
    千次阅读
    2022-04-06 17:05:59

    参考:《编译原理》
    编译过程1
    编译过程2
    编译过程3
    编译过程4

    C++编译的过程:预处理、编译、汇编、链接

    答:

    一、预编译(预处理)

    预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。

    (1)由源文件.cpp或.c生成.i文件,这是在预编译阶段完成的;(命令:gcc -E .cpp/.c —>.i)

    (2)主要功能

    1. 展开所有的宏定义,消除“#define”;
    2. 处理所有的预编译指令,比如#if、#ifdef等;
      3. 处理#include预编译指令,将包含文件插入到该预编译的位置;
    3. 删除所有的注释“/**/”、"//"等;
    4. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及错误提醒;
    5. 保留所有的#program编译指令,原因是编译器要使用它们;

    (3)缺点:不进行任何安全性及合法性检查

    二、编译

    编译过程,就是把经过预编译生成的文件,进行一系列语法分析、词法分析、语义分析优化后生成相应的汇编代码文件。

    (1)由.i文件生成.s文件,这是在编译阶段完成的;(命令:gcc -S .i —>.s)

    (2)主要功能

    1. 词法分析:
      将源代码文件的字符序列划分为一系列的记号。
      一般词法分析产生的记号有:标识符、关键字、数字、字符串、特殊符号(加号、等号);
      在识别记号的同时也将标识符放入符号表、将数字、字符放入到文字表等;
      有一个lex程序可以实现词法扫描,会按照之前定义好的词法规则将输入的字符串分割成记号,所以编译器不需要独立的词法扫描器;
      ( 如果存在括号不匹配或者表达式错误,编译器就会报告语法分析阶段的错误)

    2. 语法分析:
      语法分析器将对产生的记号进行语法分析,产生语法树----就是以表达式尾节点的树,一步步判断如何执行表达式操作。

    3. 语义分析:
      由语法阶段完成分析的并没有赋予表达式或者其他实际的意义,比如乘法、加法、减法,必须经过语义阶段才能赋予其真正的意义;
      语义分析主要分为静态语义和动态语义两种;
      静态语义通常包括声明和类型的匹配、类型的转换。比如当一个浮点型的表达式赋值给一个整型的表达式时,其中隐含了一个浮点型到整型转换的过程。只要存在类型不匹配编译器会报错。经过语义分析后的语法树的所有表达式都有了类型。
      动态语义分析只有在运行阶段才能确定。

    4. 优化后生成相应的汇编代码文件

    5. 汇总所有符号

    三、汇编

    生成可重定位的二进制文件(.obj文件)

    (1)由.s文件生成的.obj文件;(命令:gcc -c .s–>.o)

    (2)此文件中生成符号表,能够产生符号的有:所有数据都要产生符号、指令只产生一个符号(函数名);

    四、链接

    链接阶段完成后生成可执行文件.exe,链接阶段主要分为两部分:

    (1)合并所有“.obj”文件的段,并调整段偏移和段长度(按照段的属性合并,属性可以是“可读可写”、“只读”、“可读可执行”,合并后将相同属性的组织在一个页面内,比较节省空间),合并符号表,进行符号解析完成后给符号分配地址;
    其中符号解析的意思是:所有.obj符号表中对符号引用的地方都要找到该符号定义的地方。
    在编译阶段,有数据的地方都是0地址,有函数的地方都是下一行指令的偏移量-4(由于指针是4字节);
    可执行文件以页面对齐。

    符号重定位举例:main.c extern int gdata; test.c int gdata = 10;
    main.o UND gdata -------->test.o gdata //符号重定位

    在进行符号解析时要注意只对global符号进行处理,对于local符号不做处理。

    (2)符号的重定位(链接核心):将符号分配的虚拟地址写回原先未分配正确地址的地方。

    对于数据符号会存准确地址,对于函数符号,相对于存下一行指令的偏移量(从PC寄存器取地址,并且PC中下一行指令的地址)。

    五、程序运行

    这一部分主要是理解操作系统是如何管理进程和内存的。

    (1)创建虚拟地址空间到物理空间的映射(创建内核地址映射结构体),创建页目录和页表。

    (2)加载代码段和数据段。

    (3)把可执行文件的入口地址写到CPU的PC寄存器里。

    总结

    C++编译过程主要分为四个阶段,分别为预编译、编译、汇编、链接。

    预编译阶段:处理宏定义指令#define、头文件包含指令#include、条件编译指令#ifdef等,完成对源程序中伪指令和特殊符号的“替换”。

    编译阶段:主要是进行语法检查,并生成汇编代码。

    汇编阶段:主要是把汇编代码翻译成机器语言,生成目标文件.obj。

    链接阶段:主要是把目标代码(obj文件)与调用的库函数代码或自定义文件代码连接起来,形成对应的可执行文件(exe文件)。

    在这里插入图片描述

    注:
    1.编译器会将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由链接器进行链接成为一个可执行文件。
    2.在编译过程中头文件不参与编译,预编译时进行各种替换以后,头文件就完成了其光荣使命,不再具有任何作用。
    3.头文件的作用是保证当前源文件编译不会出错,头文件本身不参与编译过程。

    几个常识

    1. #include“animal.h”和#include <animal.h>的区别?

    .答:
    <>和“”表示编译器搜索头文件的顺序不同。

    <>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不会搜索当前目录。

    ""表示先从当前目录搜索,然后是在系统目录和PATH环境变量所列出的目录下搜索。
    .
    因此,如果我们知道头文件在系统目录或者环境变量目录下时,可以用<>来加快搜索速度。

    2. #include的作用?

    答:
    在编译.cpp文件时,该文件中的#include<xxx.h>会被xxx.h文件替换。

    3.头文件如何来关联其对应的源文件?

    答:
    参考:https://blog.csdn.net/sinat_36053757/article/details/64444556

    Q:
    已知头文件“a.h”声明了一系列函数,“a.cpp”中实现了这些函数,那么如果我想在“b.cpp”中使用“a.h”中声明的这些在“a.cpp”中实现的函数,通常都是在“b.cpp”中使用#include“a.h”,但是“a.h”并没有包含“a.cpp”,一般是“a.cpp”中包含“a.h”,那么b.cpp是怎样找到a.cpp中的实现呢?

    A:
    编译的时候,会编译project下所有.cpp文件,但是在编译b.cpp文件时,并不会去找a.cpp文件中的函数实现,只有在link的时候才进行这个工作。
    我们在a.cpp或b.cpp中用#include“a.h”实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。
    源文件编译后成生了目标文件(.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.obj文件(在这里是a.cpp生成的.obj文件),此时,连接器会去这个.obj文件中找在a.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件exe中。

    在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。
    这也是为什么b.cpp只包含a.h,而不用包含a.cpp,且a.h也不用包含a.cpp,b.cpp文件中包含的a.h是为了保证b.cpp文件在编译时不会出错,a.h本身不会被编译。编译器在链接时,会将a.cpp生成的目标文件链接到可执行文件exe中,这是通过Makefile完成的,并不是a.h来完成

    总结
    1.头文件的作用是保证当前源文件编译不会出错,头文件本身不参与编译。
    2.头文件相对应的cpp文件,由于在工程文件夹下,在编译阶段,编译器也会将其生成目标文件。在链接阶段时,其会靠编译器来自动链接到当前可执行文件中,而不是靠头文件。(主要把头文件对应的cpp文件放在project下,编译器在链接阶段就会自动链接)

    4.C++中的.cpp文件和.h文件

    答:
    参考:https://www.cnblogs.com/fenghuan/p/4794514.html
    头文件.h:写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。
    源文件.cpp:主要写实现头文件中已经声明的那些函数的具体代码。
    需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。

    1).h叫做头文件,它是不能被编译的。“#include”叫做编译预处理指令,可以简单理解成,在1.cpp中的#include"1.h"指令把1.h中的代码在编译前添加到了1.cpp的头部。每个.cpp文件会被编译,生成一个.obj文件,然后所有的.obj文件链接起来你的可执行程序就算生成了。

    好的习惯是,头文件中应只处理常量、变量、函数以及类等的声明,变量的定义和函数的实现等都应该在源文件.cpp中进行。

    至于.h和.cpp具有同样的主文件名的情况呢,对编译器来讲是没有什么意义的,编译器不会去匹配二者的主文件名,相反它很傻,只认#include等语句。但是这样写是一种约定俗成的编程风格,一个类的名字作为其头文件和源文件的主文件名比如Class1.h和Class1.cpp,这个类的声明在Class1.h中,实现在Class1.cpp中,我们人类看起来比较整齐,读起来方便,也很有利于模块化和源代码的重用。

    为什么这个风格会约定俗成?有一句著名的话,叫“程序是为程序员写的”。

    2)声明不会分配内存,定义才会被分配内存。在h文件中声明Declare,而在cpp文件中定义Define。 “声明”向计算机介绍名字,它说,“这个名字是什么意思”。而“定义”为这个名字分配存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”处分配存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生的指针。定义也可以是声明。如果该编译器还没有看到过名字A,程序员定义int A,则编译器马上为这个名字分配存储地址。声明常常使用于extern关键字。如果我们只是声明变量而不是定义它,则要求使用extern。对于函数声明, extern是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明。

    5. #ifdef、#else、#endif

    答:
    参考:
    参考1
    参考2

    作用:用在源文件中,选择指定代码进行编译。

    一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。

    条件编译命令最常见的形式为:

    #ifdef 标识符 
    程序段1 
    #else 
    程序段2 
    #endif
    

    它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。

    其中#else部分也可以没有,即:

    #ifdef 
    程序段1 
    #endif
    

    6. #ifndef、/#define、#endif

    答:
    参考:参考1

    作用:用在头文件中,防止该头文件被重复引用。

    被重复引用:是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。
    比如:a.h文件中有#include “c.h”,而此时b.cpp文件导入了#include “a.h” 和#include “c.h”,那么此时就会造成c.h重复引用,因为b.cpp文件中包含了两次c.h文件。

    头文件被重复引用引起的后果:有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言,编译效率低下也会需要避免的。有些头文件重复包含,会引起错误。

    使用:
    #ifndef // #ifndef A_H意思是"if not define A_H " 如果不存在A_H ,就#define A_H
    #define A_H
    #endif //否则,不执行#ifndef下面的语句,直接跳到 #endif

    含义:当a.h是第一次被调用时, #ifndef A_H成立,便定义A_H,同时头文件a.h会被调用。
    当第二次调用a.h文件时,由于 #ifndef A_H不成立,直接跳到 #endif ,那么头文件a.h就不会被调用。

    建议:所有头文件前后都加上ifndef/define/endif

    更多相关内容
  • #清磁盘啦~,CSDN“网盘”真好用,感谢CSDN~ 《编译原理》课程拓展,关于语法制导翻译和语义分析,基于语法分析树检查表达式类型是否合法的代码实现,基于课程实验的改进并借助bison和flex工具实现
  • 编译原理(四)类型检查2

    千次阅读 2018-01-25 19:44:30
    类型系统主要用来说明编程语言的定型规则,它独立于类型检查算法定义一个类型系统,一种重要的设计目标是存在有效的类型检查算法类型系统的基本概念可用于各类语言,包括函数式语言、命令式语言和并行语言等 ...

    描述类型系统的语言

    • 类型系统主要用来说明编程语言的定型规则,它独立于类型检查算法
    • 定义一个类型系统,一种重要的设计目标是存在有效的类型检查算法
    • 类型系统的基本概念可用于各类语言,包括函数式语言、命令式语言和并行语言等

    我们后面讨论用形式方法来描述类型系统

    类型系统的形式化

    类型系统是一种逻辑系统

     有关自然数的逻辑系统

    自然数表达式(需要定义它的语法)

    a+b,3

    良形公式(逻辑断言,需要定义它的语法)

    a+b=3,(d=3)^(c<10)

    推理规则

    a<b,b<c    -->  a<c

     类型系统

    类型表达式

    int,int——>int

    定型断言

    x:int|- x+3:int

    (|-左边部分x:int称为定型环境)

    类型检查和类型推断

    • 类型检查

     用语法制导的方式,根据上下文有关的定型规则来判定程序构造是否为良类型的程序构造的过程

    • 类型推断

     类型信息不完全的情况下的定型判定问题,例如:f(x:t)=E和f(x)=E的区别

    类型检查——声明语句


     编译期的类型

    整型与布尔型运算的类型转换

    类型推导:


    上面在最开始对p的类型β一无所知,随着下面返回p的类型,我们推导出p是指针类型。




    展开全文
  • 编译原理(三)类型检查

    千次阅读 2018-01-24 20:52:54
    静态检查中最典型的部分——类型检查: 类型系统、类型检查、多态函数、重载 忽略其它的静态检查:控制流检查、唯一性检查、关联名字检查 上面不能在不该出现continue的地方出现continue。 C语言: ...
    • 静态检查中最典型的部分——类型检查:

    类型系统、类型检查、多态函数、重载

    • 忽略其它的静态检查:控制流检查、唯一性检查、关联名字检查


    上面不能在不该出现continue的地方出现continue。

    C语言:

    • 称&为地址运算符,&a为变量a的地址
    • 数组名代表数组第一个元素的地址

    问题:

    如果a是一个数组名,那么表达式a和&a的值都是数组a第一个元素的地址,它们的使用是否有区别?

     用四个C文件的编译报错或运行结果来提示

    上面报错,返回的a不能作为二位数组的指针

    返回a的指针时,是正确的

    二维数组的第一个元素,可以看作是一个一维数组,所以返回a时,表示二位数组的第一个元素,与B类型相同,没有报错

    a+1加了80,因为A的第二元有20个int类型的数组,&a+1加了800,因为A占用了800字节内存。

    类型在编程语言中的作用

    执行错误和安全语言

    1、程序运行时的执行错误分成两类

    • 会被捕获的错误(trapped error)

     例:非法指令错误、非法内存访问、除数为0

     引起计算立即停止

    • 不会被捕获的错误(untrapped error)

      例如:跳到一个错误的地址,该地址开始的内存正好代表一个指令序列

     错误可能会有一段时间未引起注意

    安全语言

    • 任何合法程序都是良行为的
    • 通常是设计一个类型系统,通过静态的类型检查来拒绝不会被捕获的错误
    • 但是,设计一个类型系统,正好拒绝不会被捕获错误是非常困难的

    禁止错误

    • 不会被捕获错误集合+会被捕获错误的一个子集
    • 为语言设计类型系统的目标是在排除禁止错误

    良行为程序和安全语言也可基于禁止错误来编译

    类型化的语言

    • 变量都被给定类型的语言
    • 表达式、语句等程序构造的类型都可以静态确定
    • 例如,类型boolean的变量x在程序每次运行时的值只能时布尔值,not(x)总有意义

    类型系统

    • 语言的组成部分,它由一组定型规则(typeing rule)构成,这组规则用来给各种程序构造指派类型
    • 设计类型系统的根本目的是用静态检查的方式来保证合法程序运行时的良行为
    • 类型系统的形式化
    • ----类型表达式(描述类型的表达式)、定型断言(根据语言跟早实例判断类型)、定型规则
    • 类型检查算法
    • ----通常是静态地完成类型检查

    良类型的程序

    • 没有类型错误的程序

    类型可靠的语言

    • 所有良类型程序(合法程序)都是良行为的
    • 类型可靠的语言一定是安全的语言

    类型检查:类型化语言

    • 类型检查也可以放在运行时完成,但影响效率
    • 一般都是静态检查,类型系统被用来支持静态检查
    • 静态检查语言通常也需要一些运行时的检查
    • ----数组访问越界检查

    实际使用的一些语言并不安全

    C语言
    • 还有很多不安全的并且被广泛使用的特性,如:指针算数运算、类型强制、参数个数可变
    • 在语言的设计历史上,安全性考虑不足时因为当时强调代码的执行效率。

    在现代语言设计上,安全性越来越重要

    • C语言的一些问题已经在C++中得到缓和
    • 更多一些问题在Java中已得到解决

    类型化语言的优点

     从工程的观点看,类型化语言有下面一些优点

    • 开发的实惠
    • ----较早发现错误
    • ----类型信息还具有文档左右
    • 编译的实惠
    • ----程序模块可以相互独立地编译
    • 运行的实惠
    • ----可得到更有效的空间安排和访问方式






     



      




    展开全文
  • 编译原理的经典教程,英文版,文字版pdf。本书深入讨论了编译器设计的重要主题,包括词法分析、语法分析、语法制导分析、类型检查、运行环境、中间代码生成、代码生成、代码优化等,并在最后两章中讨论了实现编译器...
  • 龙书 编译原理第二版

    2019-03-25 09:28:25
    龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含...新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术。资源包括中/英文版,ppt课件及课后答案(英文版)。
  • 编译原理

    千次阅读 2021-06-07 06:49:04
    编译原理(计算机专业课程)编辑锁定讨论上传视频编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理...

    编译原理

    (计算机专业课程)

    编辑

    锁定

    讨论

    上传视频

    编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。 编译原理是计算机专业设置的一门重要的专业课程。编译原理课程是计算机相关专业学生的必修课程和高等学校培养计算机专业人才的基础及核心课程,同时也是计算机专业课程中最难及最挑战学习能力的课程之一。编译原理课程内容主要是原理性质,高度抽象[1]

    中文名

    编译原理[1]外文名

    Compilers: Principles, Techniques, and Tools[1]

    领    域

    计算机专业的一门重要专业课[1]

    编译原理基本概念

    编辑

    编译原理即是对高级程序语言进行翻译的一门科学技术, 我们都知道计算机程序由程序语言编写而成, 在早期计算机程序语言发展较为缓慢, 因为计算机存储的数据和执行的程序都是由0、1代码组合而成的, 那么在早期程序员编写计算机程序时必须十分了解计算机的底层指令代码通过将这些微程序指令组合排列从而完成一个特定功能的程序, 这就对程序员的要求非常高了。人们一直在研究如何如何高效的开发计算机程序, 使编程的门槛降低。[2]

    编译原理编译器

    编辑

    C语言编译器是一种现代化的设备, 其需要借助计算机编译程序, C语言编译器的设计是一项专业性比较强的工作, 设计人员需要考虑计算机程序繁琐的设计流程, 还要考虑计算机用户的需求。计算机的种类在不断增加, 所以, 在对C语言编译器进行设计时, 一定要增加其适用性。C语言具有较强的处理能力, 其属于结构化语言, 而且在计算机系统维护中应用比较多, C语言具有高效率的优点, 在其不同类型的计算机中应用比较多。[3]

    编译原理C语言编译器前端设计

    编译过程一般是在计算机系统中实现的, 是将源代码转化为计算机通用语言的过程。编译器中包含入口点的地址、名称以及机器代码。编译器是计算机程序中应用比较多的工具, 在对编译器进行前端设计时, 一定要充分考虑影响因素, 还要对词法、语法、语义进行分析。[3]

    1 词法分析[3]

    词法分析是编译器前端设计的基础阶段, 在这一阶段, 编译器会根据设定的语法规则, 对源程序进行标记, 在标记的过程中, 每一处记号都代表着一类单词, 在做记号的过程中, 主要有标识符、关键字、特殊符号等类型, 编译器中包含词法分析器、输入源程序、输出识别记号符, 利用这些功能可以将字号转化为熟悉的单词。[3]

    2 语法分析[3]

    语法分析是指利用设定的语法规则, 对记号中的结构进行标识, 这包括句子、短语等方式, 在标识的过程中, 可以形成特殊的结构语法树。语法分析对编译器功能的发挥有着重要影响, 在设计的过程中, 一定要保证标识的准确性。[3]

    3 语义分析[3]

    语义分析也需要借助语法规则, 在对语法单元的静态语义进行检查时, 要保证语法规则设定的准确性。在对词法或者语法进行转化时, 一定要保证语法结构设置的合法性。在对语法、词法进行检查时, 语法结构设定不合理, 则会出现编译错误的问题。前端设计对精确性要求比较好, 设计人员能够要做好校对工作, 这会影响到编译的准确性, 如果前端设计存在失误, 则会影响C语言编译的效果。[3]

    编译原理C语言编译器总体设计

    C语言编译器是一种先进的设备, 其可以将繁琐复杂的程序转换为机器语言, 具有化繁为简的功能, 在对C语言编译器进行划分的过程中, 需要了解语法构成原理, 设计人员需要灵活掌握语言语法知识, 还要应用C语言代码转化方式, 在对C语言编译器进行总体设计时, 需要从以下几个方面入手。[3]

    1 词法分析[3]

    C语言程序具有一定的复杂性, 语法分析是编译的初级阶段, 这一过程主要是对词法进行扫描, 所以词法分析阶段, 编译器也被用作是扫描器, 其主要的任务是将源程序中的字符进行连接, 并且对其中的词语进行识别, 并且对词汇进行转化, 将其转换为内部编码, 并且对其语法进行分析与标记。单词符号在编译的过程中, 一般会被转化为二元式, 单词类别主要有二进制、分隔符、单词等, 计算机在正常工作时, 会通过扫描器自动完成词法扫描工作, 这一过程也会自动将注释进行删除, 会将源程序中的单词进行自动识别, 还会将其转换为内部编码。从工作方式角度来分析, 编译流程与语法属于两种接口方式, 若是从功能上讲, 编译器词法分分析的过程主要就是将相应的字符源程序进行转换处理, 从而变成单词串的形式。[3]

    2 语义分析[3]

    将编译程序转换为一种内部表现形式后, 我们将该种内部表现形式称之为中间语言或者是中间代码, 它含义明确、结构简单, 属于一种记号系统。比如一些编译程序, 基本上没有中间代码, 但是为了在实际应用中, 确保机器的独立运行, 易于实现目标代码的优化, 在许多的编译程序中均设置了中间语言。这种中间语言介于机器语言和源程序语言中, 程序相对复杂, 而C语言编译器却在很大程度上改变以上现状, 其语义分析和语法分析相对成熟, 理论和算法比较完善, 可仍旧存在的问题是没有公认的形式系统, 中间代码仍旧接近于形式化的方法。[3]

    3 语法分析[3]

    语法分析主要是以单词串形式的源程序作为分析与输入对象, 其最为根本的目标和任务就是为了以设计语言的语法规则为标准, 对源程序的语法结构进行具体的分析, 根据设计语言的语法规则, 对组成这些源程序的语法成分进行分析, 如函数、下标变量、各种程序语句、各种表达式等等, 并且要通过正确性的语法检查, 将中间代码进行阶段处理。但是要注意的一点是根据需要进行了归约处理, 必然存在着相应语法错误, 那么可以将其中全部输入的符号删除, 改变上述格局, 进行移进和归约分析, 并且在此基础上, 不断地寻找一个相应的策略, 从而形成有效的语法分析方法。[3]

    编译原理编译原理课程

    编辑

    《编译原理》作为计算机专业的一门重要专业课程, 是日后深入研究专业领域知识的基础。这门课作为计算机科学与技术的专业课, 融合了离散数学、数据结构、操作系统、计算机组成原理等多个学科的知识, 属于综合性与理论性较强的一门课, 由于编译原理课程内容的以上特点, 目前在实验教学中仍存在一些问题。编译原理实验部分若要设计制作完整的编译器, 实验周期长, 涉及的模块较多, 各模块间衔接较复杂, 不易立即看到整体效果。[4]

    编译原理传统编译原理课程教学中存在的问题

    计算机类专业本科生学习本专业的第一门语言课程是C语言。C语言由于其类型不安全性, 容易出现一些难以捉摸的错误, 使得学生难以定位和解决问题。如果能让学生根据编译器提供的提示信息, 精确定位程序中的错误类型和位置, 把编译原理中所学用于实际C语言编程需求, 这既完成了课程的教学内容, 也提升了学生的软件编程和系统分析的能力。[5]

    从普通高等院校的编译原理教学实际出发, 其课程覆盖范围一般仅限于编译器的前端, 即词法分析、语法分析和语法制导翻译等内容。这其中包括大量抽象且逻辑复杂的理论知识点, 如形式语言理论、正规式、有限自动机、上下文无关文法、属性文法和语法制导翻译等。传统的教学方式强调知识点的灌输, 让学生解决孤立的单一问题, 缺乏各知识点之间的关联。这种“只见树木, 不见森林”的教学方式会极大地削弱学生的学习积极性, 导致整体效果不佳。[5]

    编译原理以实践为导向的互助式编译原理教学

    (一) 理论与实践相衔接[6]

    理论知识的来源一般都有其确定的问题背景。脱离实际问题来进行理论教学, 对学生实际能力的提升没有益处。编译原理课程中的大量理论知识, 存在一种衔接递进的关系, 每个知识点的引入和拓展, 都是对于现实遇到问题的解决路径再现。因此, 整个授课过程就在重现这种解决方案演变的变化历程。而实现这一目标的关键之处, 是教师从之前的“站到讲台前”变到现在的“坐在学生中”。这一变化绝不仅仅是简单地将所有问题留给学生, 从“讲授”变成“答疑”, 而是从问题设计、思考启迪、讨论引导到过程管理等各方面都对教师提高了要求。特别是现代高级语言发展日新月异, 各种新问题层出不穷, 如何在面对开放性的未知问题时, 从系统和整体的角度给出学生解决问题的方式方法, 而不是给出具体每个问题的回答, 这是对教师能力的一种新考验。[6]

    (二) 编译器设计实现方案[6]

    编译原理课程教学理想情况, 学生应该能够独立自主完成小型编译系统的构造。实际教学中, 学生只需吃透关键的几条原理知识, 如NFA的确定化, LL (1) 文法中FIRST和FOLLOW集合的构造,LR (1) 文法中识别活前缀DFA构造等, 基本上已经满足了课程考试要求。然而, 仅靠理论学习对实现一个基础编译器来说是远远不足的。相比较于学生对理论知识的接受程度, 学生自主动手完成编译系统的能力缺乏就更为明显。如何面对全体学生, 制定出一套适用的实践方案, 是课程实际效用的关键。[7]

    编译原理编译技术的发展

    编辑

    在早期冯诺依曼计算机时期 (20世纪40年代) 程序都是以机器语言编写, 机器语言就是实际存储的01代码, 编写程序是十分枯燥乏味的。后来汇编语言代替机器语言一符号形式该处操作指令和地址编码。但汇编语言仍有许多缺点, 阅读理解起来很难, 而且必须依赖于特定的机器, 如果想使编写好的程序在另一台计算机上运行必须重写。在20世纪50年代IBM的John Backus带领一个研究小组对FORTRAN高级语言及其编译器进行开发。编译程序的自动生成工具初现端倪, 现在很多自动生成工具已经广泛使用例如语法分析工具LEX, 语言分析程序YACC等。在20世纪60年代人们不断的用自编译技术构造编译程序, 即用被编译的语言本身来实现该语言的编译程序, 但其基本原理和结构大体相同。经过不断发展现代编译技术已经较为成熟, 多种高级语言发展迅速都离不开编译技术的进步。[2]

    编译原理编译的基本流程

    编辑

    编译可以分为五个基本步骤:词法分析、语法分析、语义分析及中间代码的生成、优化、目标代码的生成。这是每个编译器都必须的基本步骤和流程, 从源头输入高级语言源程序输出目标语言代码。[2]

    1 词法分析[2]

    词法分析器是通过词法分析程序对构成源程序的字符串从左到右的扫描, 逐个字符地读, 识别出每个单词符号, 识别出的符号一般以二元式形式输出, 即包含符号种类的编码和该符号的值。词法分析器一般以函数的形式存在, 供语法分析器调用。当然也可以一个独立的词法分析器程序存在。完成词法分析任务的程序称为词法分析程序或词法分析器或扫描器。[2]

    2 语法分析[2]

    语法分析是编译过程的第二个阶段。这阶段的任务是在词法分析的基础上将识别出的单词符号序列组合成各类语法短语, 如“语句”, “表达式”等.语法分析程序的主要步骤是判断源程序语句是否符合定义的语法规则, 在语法结构上是否正确。而一个语法规则又称为文法, 乔姆斯基将文法根据施加不同的限制分为0型、1型、2型、3型文法, 0型文法又称短语文法, 1型称为上下文有关文法, 2型称为上下文无关文法, 3型文法称为正规文法, 限制条件依次递增。[2]

    3 语义分析[2]

    词法分析注重的是每个单词是否合法, 以及这个单词属于语言中的哪些部分。语法分析的上下文无关文法注重的是输入语句是否可以依据文法匹配产生式。那么, 语义分析就是要了解各个语法单位之间的关系是否合法。实际应用中就是对结构上正确的源程序进行上下文有关性质的审查, 进行类型审查等。[2]

    4 中间代码生成与优化[2]

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

    5 目标代码的生成[2]

    根据优化后的中间代码, 可生成有效的目标代码。而通常编译器将其翻译为汇编代码, 此时还需要将汇编代码经汇编器汇编为目标机器的机器语言。[2]

    6 出错处理[2]

    编译的各个阶段都有可能发现源码中的错误, 尤其是语法分析阶段可能会发现大量的错误, 因此编译器需要做出错处理, 报告错误类型及错误位置等信息。[2]

    编译原理编译过程概述

    编辑

    C语言的源程序和对应的可执行程序执行时在内存中的运行结构,实现这一转换的最主要的过程就是编译。[8]

    源程序是给人看的,本质上就是文本文件,可以用Linux中的vi或Windows中的记事本之类的文本编辑程序打开、编写,但计算机无法直接执行源程序,需要通过一个专门的程序将源程序编译为计算机可执行程序,这个专门的程序就是编译器。编译过程主要分为词法分析、语法分析、中间代码生成、目标代码生成(忽略预处理、语义分析、优化等)。下面我们依次简要讲解编译的主要过程。[8]

    编译原理词法分析

    人眼中看到的源代码是这样的:[8]
int fun(int a,int b);

    int m=10;

    int main()

    
{

    
   int i=4;

    int j=5;

    m = fun(i,j);

    return 0;

    
}

    
int fun(int a,int b)

    {

    int c=0;

    c=a+b;

    return c;

    
}

    而它在计算机中存储的形式如图1-36所示。[8]

    bb4117a18cf998a2ad567069616163a5.png

    图1-36 案例程序的十六进制形式[8]

    这里看不出关键字、运算符、标识符,甚至看不出哪几个字符属于同一个符号。编译的第一阶段是词法分析,目的就是在连续的字符中识别出一个一个的符号,并尽可能地识别出符号的属性。[8]

    人们在看C语言源程序时,借助空格、括号等一眼就可以看出标识符、关键字。与人相比,现在计算机的智能还是相当低的,无法像人那样同时看多个字符,只能依据一个非常机械的“电子版”词法,一个字符一个字符地识别。“电子版”词法是将状态转换图的思路融汇在词法分析器的代码中得以体现的。词法分析器从源程序的字符串中识别出一个个符号(token),并按序保存。[8]

    词法分析的结果如图1-37所示。[8]

    072b0e49a743ff4cc8af9ad8b46fdc36.png

    图1-37 词法分析的结果[8]

    在词法分析阶段能够识别出一些符号的含义,它们包括关键字、数字、字符串、分隔符,这些符号的含义不需要其他符号的辅助就能确定本身的含义。比如,“int”一定代表整数类型,它不可能是一个变量名称,也不可能是一个运算符。[8]

    而另外一些符号需要通过前后的其他符号才能确定出准确含义。比如“m”,在词法阶段仅能判断这是一个标识符,但是如果不对所在的句做分析,就无法得知这个标识符代表一个变量还是一个函数。更多详细的信息需要对符号所在的句型做分析才能获得。这部分工作由语法分析来完成。[8]

    编译原理语法分析

    如果说词法分析的作用是从连续的字符中识别出标识符、关键字、数字、运算符并存储为符号(token)流,语法分析的作用就是从词法分析识别出的符号流中识别出符合C语言语法的语句。[8]

    因为计算机无法像人那样同时看多个标识符、关键字、数字、运算符,无法像人那样一眼看出这是一个函数声明,那是一个if语句,只能非常笨拙地一个符号一个符号去识别。与词法分析器有些类似,语法分析器也是依据用计算机表示的语法,一个符号一个符号地识别出符合C语言语法的语句。语法的计算机表示就是产生式。在语法分析器中把通过产生式产生的C语言语法映射成一套模板,并把这套模板融汇在语法分析器的程序中。语法分析器的作用就是将词法分析器识别出的符号(token)一个一个地与这套模板进行匹配,匹配上这套模板中的某个语法,就可以识别出一句完整的语句,并确定这条语句的语法。[8]

    我们以案例中“int fun(int a,int b);”这条函数声明语句为例描述这个过程,它与语句模板的匹配情景如图1-38所示。[8]

    64061db12dfea0f9b3e97e81d60c0c9c.png

    图1-38 fun函数声明语句与模板匹配的结果[8]

    这段token流最终与函数声明模板相匹配,在匹配成功后,计算机就认为此语句为函数声明语句,并以语法树的形式在内存中记录下来,情景如图1-39所示。[8]

    60fe9bc961ad30030e1f42c2cb4a9586.png

    图1-39 fun函数声明语句生成的语法树[8]

    以树型结构来记录分析出的语句是一个非常巧妙、深谋远虑、通盘考虑的选择。一方面,树型结构能够“记住”源程序全部的“意思”,另一方面,树型结构更容易对应到运行时结构。[8]

    编译原理从语法树到中间代码再到目标代码

    至此,语法树已经承载了源程序的全部信息,后续的转换工作就和源程序没关系了。[8]

    如果希望一步到位,从语法树转换为目标代码,理论上和实际上都是可行的。但计算机存在多种CPU硬件平台,考虑到程序在不同CPU之间的可移植性,先转换成一个通用的、抽象的“CPU指令”,这就是中间代码最初的设计思想。然后根据具体选定的CPU,将中间代码落实到具体CPU的目标代码。[8]

    语法树是个二维结构,中间代码是准一维结构,语法树到中间代码的转换过程,本质上是将二维结构转换为准一维结构的过程。中间代码特别是RTL已经可以和运行时结构一一对应了。如图1-40所示。[8]

    aeef6f7aa41bba011efceab6c6997093.png

    图1-40 中间代码与目标代码对应的情景示意[8]

    运行时结构也是一维的,图1-40左侧部分的转换结果将更贴近运行时结构。[8]

    选定具体的CPU、操作系统后,中间代码就可以转换为目标代码——汇编代码(我们配置的是AT&T汇编),这时操作系统的影响还比较小。然后由汇编器依照选定操作系统的目标文件格式,将.s文件转换为具体的目标文件,对于Linux而言是.o文件,对于Windows而言是.obj文件。目标文件中已经是选定的CPU的机器指令了。[8]

    最后链接器把一个或多个目标文件(库文件本质上也是目标文件)链接成符合选定操作系统指定格式的可执行文件。[8]

    通过操作系统,可执行程序就可以被载入内存执行,形成前两节我们所看到的运行时结构。[8]

    本书后续内容将详细讲解编译的主要过程:词法分析、语法分析、中间代码到目标代码,然后是汇编与链接,最后讲解预处理。[8]

    词条图册

    更多图册

    参考资料

    2.

    刘雪飞.浅谈编译原理[J].中外企业家

    .中国知网.2018-01-25[引用日期2020-04-23]

    4.

    万新燕,时招军.编译原理实验教学设计[J].教育教学论坛

    .中国知网.2019-02-20[引用日期2020-04-23]

    6.

    蒋宗礼.编译课程教材建设[J].计算机教育

    .中国知网.2007-06-10[引用日期2020-04-12]

    8.

    新设计团队.编译系统透视:图解编译原理:机械工业出版社,2016-01

    展开全文
  • 类型检查 ④错误处理 4.1.2 相关约定 符号的使用约定 终结符 ①.字母表中比较靠前的小写字,如a,b,c ②. 操作符,如+、-等 ③. 标点符号,如括号、逗号等 ④. 数字0、1、。。。9 ⑤. 黑体串,如if 、id等 非终结符 ...
  • 文章目录第一节 引言1.... 幂集第四节 C语言的数据类型第五节 类型检查第六节 类型转换第七节 类型等价第八节 实现模型 第一节 引言 具体和抽象 具体:二进制串(机器语言) 抽象:符号注记(汇编语言,高...
  • 编译原理 龙书

    2017-09-20 15:27:21
    本书深入讨论了编译器设计的重要主题,包括词法分析、语法分析、语法制导分析、类型检查、运行环境、中间代码生成、代码生成、代码优化等,并在最后两章中讨论了实现编译器的一些编程问题和几个编译器实例,每章都...
  • java编译原理

    千次阅读 2020-06-10 15:01:37
    ),final变量是否不会被重复赋值,方法的返回值类型是否确定,检查异常是否已捕获或向上抛出,是否存在不会被执行的语句,消除无效语句(如永远为false的判断),解除语法糖(如foreach改为标准for循环,变量的自动...
  • 1、龙书(Dragon book) 英文名:Compilers: Principles,Techniques,and Tools ...新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术。本书深入讨论了编译器设计的重要主题,
  • 编译原理概述

    千次阅读 2022-03-23 15:50:14
    概述 编译本质上就是吧源代码翻译成目标代码的过程 编译分为6个阶段 词法分析 语法分析 语义分析 生成中间代码 ...词法分析就是把字符串转化为Token的一...目的是让编译器对这对Token进行语法分析,检查是否有语法错
  • 编译原理实验decaf_PA2

    2013-04-28 17:13:13
    清华大学计算机系编译原理实验。实验第二部分
  • 该词法分析器能够实现:有正确的单词流输出(类型码、坐标、单词的值);能够检查词法是否有错误(输出词法错误如果没有错误,返回 0,如果有错误,需要报告词法错误在源程序中的位置。能够越过错误,分解下一个单词...
  • 1、参考书籍:《编译原理》第三版 清华大学出版社 1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正) 2、本文目的:开源共享 抛砖引玉 一起学习 3...
  • 非终结符号 D 实现定义两种类型int, real 变量的声明; .. 非终结符号 S 实现变量之间的*,+,: =(赋值运算) .. 两个关键字 int 和real .. 变量之间的*,+,/,: =(赋值) 运算只能使用声明过的变量,所以要 检查...
  • 编译原理 合工大17级 课件 李宏芒老师的课件 包含以下章节 第一章 引论  1.1 什么叫编译程序  1.2 编译过程概述  1.3 编译程序的结构  1.4 编译程序与程序设计环境  1.5 编译程序的生成 第二章 高级语言及其...
  • 编译原理 龙书第2版

    2018-05-18 17:41:13
    编译原理 龙书第2版龙书”。龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有...新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术
  • 编译原理之语义分析

    千次阅读 2020-10-28 13:55:09
    根据类型检查是在编译期还是在运行期进行的,我们可以把计算机语言分为两类: 静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)。 动态类型语言(类型的检查是在运行期进行的)。 类型系统 高级语言为...
  • 龙书”。龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有反映一些新的编译技术...新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术
  • 编译原理(龙书)

    2014-07-12 16:13:54
    包含中文版和英文版,龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有反映一些...新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术。
  • 1、龙书(Dragon book) 英文名:Compilers: Principles,Techniques,and Tools 作者:Alfred V.Aho,Ravi Sethi,Jeffrey D....新编的《编译原理》抛弃诸如算符优先分析等过时技术,增加面向对象编译、类型检查等新技术
  • 编译原理书籍推荐

    千次阅读 多人点赞 2018-09-28 13:44:20
    大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,可是编译原理却一直作为大学本科的必修课程,同时也成为了研究生入学考试的必考内容。编译原理及...
  • 编译原理知识点

    千次阅读 多人点赞 2019-05-16 09:15:52
    1. 编译程序(编译器):先将源程序翻译成汇编语言程序或机器语言程序(称为目标程序),然后再执行它。 2. 解释程序(解释器):按解释方式进行翻译的翻译程序称为解释程序。解释程序的主要优点是便于对源程序进行...
  • HUST网安|编译原理实验|实验四攻略

    千次阅读 多人点赞 2021-12-08 10:05:55
    助力来年编译原理加大难度!(hhh) MiniC语法分析及中间代码生成 我根据我的实验报告重置了攻略。 贴个完成时间。 文章目录 MiniC语法分析及中间代码生成 实验内容 实验过程 LLVM IR初识 LLVM IR API 语义分析与中间...
  • 编译原理全套

    2011-12-03 11:17:21
    5.2.3 类型检查和类型推断 5.3 简单类型检查器的说明 5.3.1 一个简单的语言 5.3.2 类型系统 5.3.3 类型检查 5.3.4 类型转换 *5.4 多态函数 5.4.1 为什么要使用多态函数 5.4.2 类型变量 5.4.3 一个含多态函数...
  • 书中深入讨论了编译器设计的重要主题,包括词法分析、语法分析、语法制 导分析、类型检查、运行环境、中间代码生成、代码生成、代码优化等,并在 最后两章中讨论了实现编译器的一些编程问题和几个编译器实例,而且...
  • 编译原理面试总结

    千次阅读 多人点赞 2020-07-22 22:38:57
    文章目录1、编译过程概述(1)词法分析(1)语法分析(2)语义分析(3)中间代码生成(4)代码优化(5)目标代码生成(6)表格管理程序(7)出错处理3.前端、后端, 分析与综合2、文法和语言(1).句型、句子、...
  • 本书深入讨论了编译器设计的重要主题,包括词法分析、语法分析、语法制导分析、类型检查、运行环境、中间代码生成、代码生成、代码优化等,并在最后两章中讨论了实现编译器的一些编程问题和几个编译器实例,每章都...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 144,311
精华内容 57,724
热门标签
关键字:

编译原理类型检查