精华内容
下载资源
问答
  • C语言编译的基本原理

    2018-01-25 16:23:30
    C语言编译的基本策略是使用程序将源代码文件转换为可执行文件。 这个过程分为三部分: 源代码文件 ------> 目标代码文件------>可执行文件 用到两个组件: 编译器、链接器。 编译器的作用是将源代码转换为中间...

    C语言编译的基本策略是使用程序将源代码文件转换为可执行文件。

    这个过程分为三部分:

    源代码文件 ------> 目标代码文件------>可执行文件

    用到两个组件:

    编译器、链接器。

    编译器的作用是将源代码转换为中间代码,产生中间文件。

    链接器将此中间代码与其他代码相结合来生成可执行文件。

     

    中间文件的形式有多种,一般就是将源代码文件转换为机器语言代码,将其结果放置在一个目标代码文件中。虽然目标代码文件包含机器代码文件,但是该文件还不能运行。目标文件包含源代码的转换结果,但它还不是一个完整的程序,也就是不是一个完整的可执行文件,它还需要与一些基本元素。

    目标代码文件中所缺少的第一个元素是一种叫做启动代码的东西,这个代码相当于程序跟操作系统之间的接口。

    所缺少的第二个元素是库例程的代码,几乎所有c程序都利用标准c库中所包含的例程,例如printf。

    而链接器的作用就是将这三部分结合在一起,并将它们存放在单个文件,即可执行文件中,这样,一个完整的可执行文件就产生了。

    简而言之,目标文件只包含源代码转换成的机器语言,而可执行文件包含所使用的的库例程和启动代码的机器代码。

    展开全文
  • 编译原理是计算机专业一门重要专业课,旨在介绍编译程序构造一般原理基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。 编译原理是计算机...
  • C语言编译过程简介

    2017-09-21 16:44:35
    C语言编译过程简介 刚开始接触编程时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序结果就会在那个黑色屏幕上。现在找了个机会将C语言编译原理做一下小小...

    C语言编译过程简介

    刚开始接触编程的时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序的结果就会在那个黑色的屏幕上。现在找了个机会将C语言的编译原理做一下小小的总结,这样也能为以后我们进军linux编程做一些准备工作,现在这里和大家一起分享分享。O(_)O~

    讲到编译原理,我觉得首先我们得明白一些基本概念。

    1.                   编辑器:我们编写代码的一些窗口,如:记事本、wordnotepad 等。

    2.                   编译器:检查用户代码的一些语法错误并且将其编译成汇编代码。

    3.                   汇编器:将编译出来的文件变成目标代码(windows 下的.obj文件)

    4.                   连接器:将目标代码连接成为可执行文件(.exe),及双击就可以运行文件。

    5.                   集成开发环境(Integrated Development Environment,简称IDE):是用于程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面工具。如:VC6.0C_Free等。

    好了,下面大家来看看整个过程吧:如图片
    Ok!现在是不是对程序的编译有一点概念了,O(∩_∩)O~。现在稍微总结一下编译的完整过程吧:
           C源程序-->预编译处理(.c)-->编译、优化程序(.s.asm)-->汇编程序(.obj.o.a.ko)-->链接程序(.exe.elf.axf等)。
    好了,现在我们就来逐条了解一下每个过程吧。
    1.      C源程序
                 这个大家都清楚了,那就是自己编写程序代码。
    2.  预编译处理(.c)
    它主要包括四个过程
    a、宏定义指令,如#define N 6,#undef等。
                 对于前一个伪指令,预编译所要做的是将程序中的所有N用6替换,请大家注意这里是替换,并不是像作为函数参数那样将6复制进N这个变量。对于后者,则将取消对某个宏的定义,使以后出现的N不再被替换。
                 b、条件编译指令,如#ifdef,#ifndef,#endif等。
                 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。这样就能在编译阶段减少编译时间,提高效率,看看这是多好的指令。O(∩_∩)O~
                  c、 头文件包含指令,如#include "file.h"或#include <file.h>等。
                   在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
             采用这样的做法一来可以让我们直接调用一些复杂库函数;二来可以免去我们在写程序时重复做一些定义声明工作的麻烦。试想一下,一旦我们写好头文件,那么以后要用到相关模块就再也不用写这些函数了,直接#include 就OK了,这可是一劳永逸啊,天大的便宜呢,呵呵。
                 对了,这里顺便提一下#include<>与#include“”的区别。
                 #include<>:这条指令就是告诉编译器去系统默认的路径寻找相关文件。
    #include”” :这条是告诉编译器先去源程序所在目录下寻找,如果没有就去系统默认路径寻找。
                d、特殊符号,预编译程序可以识别一些特殊的符号。
                 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序就是对在源程序中出现的这些特殊符号将用合适的值进行替换。
       大家注意到没,预编译阶段基本上是完成对源程序的相关代码进行替换,这样之后程序的原意没有改变,就是代码的内容有所不同,这样为以后的编译做好准备,ok,第二阶段完满结束,嘿嘿!
    3.      编译、优化程序(.s.asm
                 经过上一阶段的处理,现在我们的程序已经没有宏定义,包含头文件等指令了,只剩下一些变量,常量,关键字等,而编译的主要作用是检查这些代码的语法错误及将这些代码编译成为汇编文件。
                 优化程序这是很复杂的,不仅和编译技术本身有关,还和目标板相应的硬件环境有很大的关系。如下面的代码:
    int a,b,c; 
    a = inWord(0x100); /*读取 I/O 空间 0x100 端口的内容存入 a 变量*/ 
    b = a; 
    a = inWord (0x100); /*再次读取 I/O 空间0x100端口的内容存入 a 变量*/ 
    c = a;
    很可能被编译器优化为:
    int a,b,c; 
    a = inWord(0x100); /*读取 I/O 空间 0x100 端口的内容存入 a 变量*/ 
    b = a; 
    c = a;
    也正是由于这种编译器优化作用才使关键字volatile有了这么大的用武之地,当然这只是原因之一。
    4.      汇编程序(.obj.o.a.ko)
    在这个阶段是将汇编代码翻译成目标文件,这时的文件已经是二进制代码了。在windows环境下文件的后缀名是.obj;而在unix下则有是o、.a、.ko等文件。
    目标文件由段组成。通常一个目标文件中至少有两个段:
              代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
         数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
    5.      链接程序(.exe.elf.axf
    也许有人会有疑问,上面的目标代码已经是机器码了,也就是说CPU可以识别这些文件了,那为什么我们还要链接程序呢?大家想想我们是不是忘了点什么。。。对!那些被包含的头文件,以及当我们的程序分布于很多源文件时,那么这些源文件该怎么处理呢,这就是连接器的作用,它们被翻译成目标代码后需要被链接到一起才能被执行。这样就ok了,嘿嘿!
    谈到函数库的链接,我们还需要了解点函数库的知识,函数库分静态链接库(又称静态库*.lib)和链接动态库(又称动态库*.dll)。
    静态库的链接在编译时会被编译进汇编文件,这样的操作会改变文件大小;而动态库则是在执行时(双击运行),当需要动态库中的文件时才被链接到可执行文件的。

                   Ok!总结就到这里吧,希望对大家能有所帮助,O(∩_∩)O~~~~




    转载来自:http://blog.csdn.net/chengocean/article/details/6250779

    展开全文
  • C语言编译过程简介 C语言编译过程简介 刚开始接触编程时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序结果就会在那个黑色屏幕上。现在找了个机会将C语言编译...

    C语言编译过程简介

    刚开始接触编程的时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序的结果就会在那个黑色的屏幕上。现在找了个机会将C语言的编译原理做一下小小的总结,这样也能为以后我们进军linux编程做一些准备工作,现在这里和大家一起分享分享。O(∩_∩)O~

    讲到编译原理,我觉得首先我们得明白一些基本概念。

    1.                   编辑器:我们编写代码的一些窗口,如:记事本、word、notepad 等。

    2.                   编译器:检查用户代码的一些语法错误并且将其编译成汇编代码。

    3.                   汇编器:将编译出来的文件变成目标代码(windows 下的.obj文件)

    4.                   连接器:将目标代码连接成为可执行文件(.exe),及双击就可以运行文件。

    5.                   集成开发环境(Integrated Development Environment,简称IDE):是用于程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面工具。如:VC6.0、C_Free等。

    好了,下面大家来看看整个过程吧:如图片 
    
    Ok!现在是不是对程序的编译有一点概念了,O(∩_∩)O~。现在稍微总结一下编译的完整过程吧:
           C源程序-->预编译处理(.c)-->编译、优化程序(.s、.asm)-->汇编程序(.obj、.o、.a、.ko)-->链接程序(.exe、.elf、.axf等)。
    好了,现在我们就来逐条了解一下每个过程吧。
    1.      C源程序
                 这个大家都清楚了,那就是自己编写程序代码。
    2.  预编译处理(.c)
    它主要包括四个过程
    a、宏定义指令,如#define N 6,#undef等。
                 对于前一个伪指令,预编译所要做的是将程序中的所有N用6替换,请大家注意这里是替换,并不是像作为函数参数那样将6复制进N这个变量。对于后者,则将取消对某个宏的定义,使以后出现的N不再被替换。
                 b、条件编译指令,如#ifdef,#ifndef,#endif等。
                 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。这样就能在编译阶段减少编译时间,提高效率,看看这是多好的指令。O(∩_∩)O~
                  c头文件包含指令,如#include "file.h"或#include <file.h>等。
                   在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
             采用这样的做法一来可以让我们直接调用一些复杂库函数;二来可以免去我们在写程序时重复做一些定义声明工作的麻烦。试想一下,一旦我们写好头文件,那么以后要用到相关模块就再也不用写这些函数了,直接#include 就OK了,这可是一劳永逸啊,天大的便宜呢,呵呵。
                 对了,这里顺便提一下#include<>与#include“”的区别。
                 #include<>:这条指令就是告诉编译器去系统默认的路径寻找相关文件。
    #include”” :这条是告诉编译器先去源程序所在目录下寻找,如果没有就去系统默认路径寻找。
                d、特殊符号,预编译程序可以识别一些特殊的符号。
                 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序就是对在源程序中出现的这些特殊符号将用合适的值进行替换。
       大家注意到没,预编译阶段基本上是完成对源程序的相关代码进行替换,这样之后程序的原意没有改变,就是代码的内容有所不同,这样为以后的编译做好准备,ok,第二阶段完满结束,嘿嘿!
    3.      编译、优化程序(.s、.asm
                 经过上一阶段的处理,现在我们的程序已经没有宏定义,包含头文件等指令了,只剩下一些变量,常量,关键字等,而编译的主要作用是检查这些代码的语法错误及将这些代码编译成为汇编文件。
                 优化程序这是很复杂的,不仅和编译技术本身有关,还和目标板相应的硬件环境有很大的关系。如下面的代码:
    int a,b,c; 
    a = inWord(0x100); /*读取 I/O 空间 0x100 端口的内容存入 a 变量*/ 
    b = a; 
    a = inWord (0x100); /*再次读取 I/O 空间0x100端口的内容存入 a 变量*/ 
    c = a;
    很可能被编译器优化为:
    int a,b,c; 
    a = inWord(0x100); /*读取 I/O 空间 0x100 端口的内容存入 a 变量*/ 
    b = a; 
    c = a;
    也正是由于这种编译器优化作用才使关键字volatile有了这么大的用武之地,当然这只是原因之一。
    4.      汇编程序(.obj、.o、.a、.ko)
    在这个阶段是将汇编代码翻译成目标文件,这时的文件已经是二进制代码了。在windows环境下文件的后缀名是.obj;而在unix下则有是o、.a、.ko等文件。
    目标文件由段组成。通常一个目标文件中至少有两个段:
              代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
         数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
    5.      链接程序(.exe、.elf、.axf
    也许有人会有疑问,上面的目标代码已经是机器码了,也就是说CPU可以识别这些文件了,那为什么我们还要链接程序呢?大家想想我们是不是忘了点什么。。。对!那些被包含的头文件,以及当我们的程序分布于很多源文件时,那么这些源文件该怎么处理呢,这就是连接器的作用,它们被翻译成目标代码后需要被链接到一起才能被执行。这样就ok了,嘿嘿!
    谈到函数库的链接,我们还需要了解点函数库的知识,函数库分静态链接库(又称静态库*.lib)和链接动态库(又称动态库*.dll)。
    静态库的链接在编译时会被编译进汇编文件,这样的操作会改变文件大小;而动态库则是在执行时(双击运行),当需要动态库中的文件时才被链接到可执行文件的。
                   Ok!总结就到这里吧,希望对大家能有所帮助,O(∩_∩)O~~~~

    转载于:https://www.cnblogs.com/njczy2010/p/5793480.html

    展开全文
  • 上一节7的编译树仅仅支持算术表达式+-基本运算,本节对其进行扩充,使其支持不仅支持基本的算术运算,还支持变量,支持语句(if, while,input, ouput),经过这次扩充,它形成语法树已经基本具备了表达minus-c语言...

    前言

    这个编译原理是一个系列,系列地址为: https://blog.csdn.net/lpstudy/article/category/937055
    考虑到很多小伙伴咨询代码的问题,现把链接发出来:https://github.com/lpstudy/compile
    这个链接里面具有这个系列所有的VS工程和代码,工程是按照系列中的一个教程环境配置6来配置的,不过lib我好像没有上传到github。
    如果大家发现任何问题,可以在github或者csdn,我有空的时候完善一下,争取做到下载github工程即可跑。

    简介

    本章在上一节7的基础上对编译树进行完善。 上一节7的编译树仅仅支持算术表达式的+-基本运算,本节对其进行扩充,使其支持不仅支持基本的算术运算,还支持变量,支持语句(if, while,input, ouput),经过这次扩充,它形成的语法树已经基本具备了表达minus-c语言的能力。

    简单说来,它可以表示下面的c语言代码:

    a = 1
    if(a>10){
      a = 11
    }else{
      a = 7
    }
    print(a)
    
    a =1
    sum = 0
    while(a <= 10){
       sum = sum + a
       a = a+1;
    }
    print(sum)
    

    根据上述的c语言代码,通过手动构建对应的语法树,然后后序遍历语法树,就可以执行代码,输出结果。本节的编译树代码并不关注如何根据c语言代码自动构建语法树(lex和yacc支持), 而是着眼于对手动构建的语法树进行遍历执行。

    语句的处理

    if语句

    if(expr) {stmt1} else  {stmt2}
    

    这里写图片描述
    对于if树节点,它具有3个孩子,expr,stmt1,stmt2. 当后序遍历到if节点时候,它首先判断expr是否为真,如果为真,则执行stmt1,否则执行stmt2,因此当执行后序遍历的时候,我们会有这样的代码:

    if(t->kind == STMT_NODE){
    			if(t->kind_kind == IF_STMT){
    				//if条件判断结果,第二个孩子存储if成功的代码,第三个孩子存储else成功的代码
    				recursive_execute(t->children[0]);
    				if (my_mem[t->children[0]->addr] )
    					recursive_execute(t->children[1]);
    				else
    					recursive_execute(t->children[2]);
    			}//IF_STMT
    }//STMT_NODE
    

    如上面的代码所示,首先根据t->kind判断是否是语句节点,然后根据子类型t->kind_kind判断是否是if节点。如果是的话,首先递归执行第一个孩子,执行完毕后,结果保存在my_mem[t->children[0]->addr]中,然后判断它的值是否为真,如果为真,则执行stmt1,否则执行stmt2。

    while语句

    while(expr)  {stmt}
    

    与if不同的是,while只有两个孩子,expr和stmt, 同时当expr为真的时候,stmt会循环执行,直到expr为假。因此遍历代码如下:

    if(t->kind_kind == WHILE_STMT){
    	//第一个孩子存储条件判断结果,第二个孩子存储while成功的代码
    	recursive_execute(t->children[0]);
    	while (my_mem[t->children[0]->addr])
    	{
    		recursive_execute(t->children[1]);
    		recursive_execute(t->children[0]);
    	}
    }
    

    输入输出语句

    输入输出相当于修改或者打印给定节点对应的内存的值,因此就非常简单,代码如下:

    else if(t->kind_kind == INPUT_STMT){//input statement has one expr child
    	cout<<"please input data:";
    	cin>>my_mem[t->children[0]->addr];
    }else if(t->kind_kind == PRINT_STMT){//print statement has one expr child to print.
    	recursive_execute(t->children[0]);
    	cout<<my_mem[t->children[0]->addr];
    }
    

    复合语句

    复合语句是用来包装多个简单语句的,例如如果有3个语句,它们之间是顺序执行的关系,但是最后生成是一棵树,因此需要将3个语句组合在一起,简单来说就是将这3个语句的Node作为一个复合语句Node的孩子,当遍历执行的时候,只需要逐个执行每一个孩子即可。
    这里写图片描述

    代码如下:

    else if(t->kind_kind == COMP_STMT){//组合语句,逐个语句执行即可。
    	for (int i = 0; i < MAX_CHILDREN; ++i)
    		recursive_execute(t->children[i]);
    }
    

    表达式语句

    表达式语句就是基本的表达式后面加上分号,例如b=1;这就是一个语句,它是一个赋值表达式然后加上分号构成的语句。为了简单起见,对于表达式语句,它只有一个孩子就是表达式,因此表达式语句的执行就是执行它的孩子(表达式)

    代码如下:

    else if(t->kind_kind == EXPR_STMT){
    	recursive_execute(t->children[0]);
    }
    

    表达式的处理

    表达式有很多种,例如二元的加减乘除,逻辑运算与或非,比较运算符,一元的取非,自增自减等等,以及还是纯粹的数字,变量表达式。简单起见,本代码只考虑支持基本的二元运算以及数字和变量表达式,它的执行代码比较简单,不具体阐述,示意如下:

    if (t->kind == EXPR_NODE){			// 表达式结点
    	recursive_execute(t->children[0]);
    	recursive_execute(t->children[1]);
    		
    	if (t->kind_kind == OP_EXPR) {		// 运算类型表达式
    		if (t->attr.op == PLUS)			// 加法表达式
    			// 从内存(my_mem)中取出两个孩子的值,进行加法,结果写回内存
    			my_mem[t->addr] = my_mem[t->children[0]->addr] + my_mem[t->children[1]->addr]; 
    		else if (t->attr.op == MINUS)	// 减法的处理类似加法
    			my_mem[t->addr] = my_mem[t->children[0]->addr] - my_mem[t->children[1]->addr];
    		else if (t->attr.op == TIMES)
    			my_mem[t->addr] = my_mem[t->children[0]->addr] * my_mem[t->children[1]->addr];
    		else if (t->attr.op == OVER){
    			if(my_mem[t->children[1]->addr] == 0){
    				cout<<"error: divide by zero"<<endl;
    				my_mem[t->addr] = 0;
    			}else{
    				my_mem[t->addr] = my_mem[t->children[0]->addr] / my_mem[t->children[1]->addr];
    			}
    		}
    		else if (t->attr.op == AND)
    			my_mem[t->addr] = my_mem[t->children[0]->addr] && my_mem[t->children[1]->addr];
    		else if (t->attr.op == OR)
    			my_mem[t->addr] = my_mem[t->children[0]->addr] || my_mem[t->children[1]->addr];
    		else if (t->attr.op == EQ)
    			my_mem[t->addr] = (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    		else if (t->attr.op == GT)
    			my_mem[t->addr] = my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr];
    		else if (t->attr.op == LT)
    			my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]);
    		else if (t->attr.op == NE)
    			my_mem[t->addr] = !(my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    		else if (t->attr.op == GE)
    			my_mem[t->addr] = (my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    		else if (t->attr.op == LE)
    			my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    		else if(t->attr.op == ASSIGN){
    			my_mem[t->addr] = my_mem[t->children[0]->addr] = my_mem[t->children[1]->addr];
    		}
    	}
    	else if (t->kind_kind == CONST_EXPR)	// 常量表达式,将值(在vali中)保存至分配的内存中
    		my_mem[t->addr] = t->attr.vali;
    	else if(t->kind_kind == ID_EXPR){
    		//do nothing
    	}
    
    }//EXPR_NODE
    

    其中比较特别就是对于ID_EXPR(变量)类型,不作处理。这是因为此代码并没有支持符号表,变量节点的内存直接存储的是变量的值,而不是如正规的符号表中存储是变量所处于的符号表的位置。 当碰到ID_EXPR类型,不需要进行额外的处理,变量的内存更新由具体的赋值运算决定。

    树构建代码

    先针对一个具体的while例子,说明如何构建一颗语法树,可以表达while语句。

    要表达的while语句如下:

    	a =1
    	sum = 0
    	while(a <= 10){
    	   sum = sum + a
    	   a = a+1;
    	}
    	print(sum)
    

    它包含2个变量,输出结果是1+2+…+10。

    对应的构建语法树的代码:

    void whileTest()
    {
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t,*p1,*q1;
    	p = createId(expr);//a
    	q = createId(expr);//sum
    	p1 = createAssignStmt(expr,p, 1);//a=1
    	q1 = createAssignStmt(expr, q, 0);//sum=0
    
    	Node *a, *b, *c, *d, *e, *f, *g;
    	a = createOpExpr(expr, p, createConst(expr, 10), LE);//a<=10
    	b = createOpExpr(expr, p, q, PLUS);//sum+a
    	c = createAssignStmt(expr, q, b);//sum = sum+a
    
    	d = createOpExpr(expr, p, createConst(expr, 1), PLUS);
    	e = createAssignStmt(expr, p, d);	//a=a+1
    	f = createCompStmt(expr, c, e); //{sum = sum+a, a= a+1}
    	r = createWhileStmt(expr, a,  f);//while
    
    	t = createOutStmt(expr, q);//print sum
    
    	o = createCompStmt(expr, p1, q1, r, t);
    
    	executeTree(expr, o);
    }
    

    上面有很多createXXX的函数,并不需要关注其具体实现也可以读懂上面的构建代码。
    首先创建两个变量,然后创建两个赋值语句p1p1p1, q1q1q1,随后创建while语句rrr和print语句ttt,最后将444个语句组装成一个组合语句ooo并将ooo作为语法树的根,启动execute执行。

    实现代码

    考虑到便于大家自己扩充,我将我自己扩充的版本放在这里,大家可以根据自己的想法继续扩充,例如实现多种一元运算符,实现for循环等等。大家将下面的代码直接粘贴到vs工程中,编译就可以执行。注意main函数中有多种test,可以专注于某一种情况的测试。

    #include <iostream>
    #include <malloc.h>
    using namespace std;
    
    #define  MAX_CHILDREN 4
    int my_mem[100];			// “内存”
    int offset;
    
    enum					// 结点类型——kind
    {
    	STMT_NODE = 0,
    	EXPR_NODE,
    	DECL_NODE
    };
    
    enum					// 语句结点子类型——kindkind
    {
    	IF_STMT = 0,
    	WHILE_STMT,
    	INPUT_STMT,
    	PRINT_STMT,
    	COMP_STMT,
    	EXPR_STMT,  //只是一个包装而已
    };
    
    enum					// 表达式结点子类型——kindkind
    {
    	TYPE_EXPR = 0,
    	OP_EXPR,
    	NOT_EXPR,
    	ARRAY_EXPR,
    	CONST_EXPR,
    	ID_EXPR,
    };
    
    enum					// 声明结点子类型——kindkind
    {
    	VAR_DECL = 0,
    	ARRAY_DECL
    };
    
    enum					// 运算——op
    {
    	//加减乘除,算术运算符
    	PLUS = 0,
    	MINUS,
    	TIMES,   
    	OVER,
    	//与或,逻辑运算符
    	AND,
    	OR,
    	//比较运算符
    	EQ,
    	LT,
    	GT,
    	GE,
    	LE,
    	NE,
    	ASSIGN,
    };
    enum
    {
    	Integer = 0,
    };
    
    union NodeAttr {
    	int op;				// 表达式结点,子类型是运算类型时,用op保存具体运算
    	double vali;				// 表达式结点,常量表达式时,用vali保存整型常量值
    	char valc;			// 字符值
    
    	NodeAttr(void) { op = 0; }		// 几种构造函数
    	NodeAttr(int i)	{ op = i; }
    	NodeAttr(double i)	{ vali = i; }
    	NodeAttr(char c) { valc = c; }
    };
    
    
    struct Node
    {
    	struct Node *children[MAX_CHILDREN];	// 孩子结点
    	int kind;					// 结点类型
    	int kind_kind;				// 子类型
    	NodeAttr attr;				// 结点属性
    	int addr;					// 分配的内存空间(数组下标)
    };
    
    
    
    class tree	// 语法树类
    {
    private:
    	Node *root;			// 根结点
    
    private:
    	void recursive_get_addr(Node *t);	// 为临时变量(如表达式)分配存储空间
    	void recursive_execute(Node *t);	// 遍历树,执行源程序
    
    public:
    	void setRoot(Node* p){root = p;}
    	Node *NewRoot(int kind, int kind_kind, NodeAttr attr, int type,
    		Node *child1 = NULL, Node *child2 = NULL, Node *child3 = NULL, Node *child4 = NULL);					// 创建一个结点,设置其属性,连接孩子结点
    	void get_addr(void);		// 分配空间和执行代码的接口
    	void execute(void);
    };
    
    Node * tree::NewRoot(int kind, int kind_kind, NodeAttr attr, int type,
    			  Node *child1, Node *child2, Node *child3 , Node *child4)
    {
    	Node* node = new Node();
    	node->kind = kind;
    	node->kind_kind = kind_kind;
    	node->attr = attr;
    	node->children[0] = child1;
    	node->children[1] = child2;
    	node->children[2] = child3;
    	node->children[3] = child4;
    
    	return node;
    }
    
    void tree::get_addr(void)
    {
    	cout << "allocate memory..." << endl;
    	offset = 0;
    	recursive_get_addr(root);		// 接口函数直接调用实际分配空间的递归函数
    }
    
    
    
    void tree::recursive_get_addr(Node *t)
    {
    	if (t) {		// 空指针什么也不做
    		if (t->kind == EXPR_NODE) {	// 为表达式结点分配存储空间
    			t->addr = offset++;
    			//cout << t->addr << endl;
    		}
    		for (int i = 0; i < MAX_CHILDREN; i++)	// 递归处理所有子树——先序遍历
    			recursive_get_addr(t->children[i]);
    	}
    }
    
    void tree::execute(void)
    {
    	cout << "execute..." << endl;
    	recursive_execute(root);				// 接口函数调用递归函数
    	//cout << my_mem[root->addr] << endl;	// 从内存取出执行结果,输出
    }
    /*
    功能逐步添加摘要:
    if条件判断功能:
    根据if的condition来决定是否执行statement代码,因此不能首先对其所有的孩子进行后续遍历,需要根据第一个孩子的执行结果来决定是否执行第二个孩子。
    while循环功能:
    此功能框架代码与if功能相似,只是多了一个while循环而已
    变量赋值:
    读取用户输入,并赋值到变量,支持input(a),它将用户的输入赋值到变量a,a为其第一个孩子。
    赋值语句:
    支持a=2,包括两个孩子,左边为变量,右边为值,当前值为左边的值。
    输入功能:
    能够接收用户输入,并赋值到变量中,前提需要增加赋值功能
    输出功能:
    构建输出语句,它只有一个孩子,即要输出的变量。
    */
    void tree::recursive_execute(Node *t)
    {
    	if (t) {
    		if(t->kind == STMT_NODE){
    			if(t->kind_kind == IF_STMT){
    				//if条件判断结果,第二个孩子存储if成功的代码,第三个孩子存储else成功的代码
    				recursive_execute(t->children[0]);
    				if (my_mem[t->children[0]->addr] )
    					recursive_execute(t->children[1]);
    				else
    					recursive_execute(t->children[2]);
    			}else if(t->kind_kind == WHILE_STMT){
    				//第一个孩子存储条件判断结果,第二个孩子存储while成功的代码
    				recursive_execute(t->children[0]);
    				while (my_mem[t->children[0]->addr])
    				{
    					recursive_execute(t->children[1]);
    					recursive_execute(t->children[0]);
    				}
    			}else if(t->kind_kind == INPUT_STMT){//input statement has one expr child
    				cout<<"please input data:";
    				cin>>my_mem[t->children[0]->addr];
    			}else if(t->kind_kind == PRINT_STMT){//print statement has one expr child to print.
    				recursive_execute(t->children[0]);
    				cout<<my_mem[t->children[0]->addr];
    			}else if(t->kind_kind == COMP_STMT){//组合语句,逐个语句执行即可。
    				for (int i = 0; i < MAX_CHILDREN; ++i)
    					recursive_execute(t->children[i]);
    			}else if(t->kind_kind == EXPR_STMT){
    				recursive_execute(t->children[0]);
    			}
    		}
    		if (t->kind == EXPR_NODE){			// 表达式结点
    			recursive_execute(t->children[0]);
    			recursive_execute(t->children[1]);
    				
    			if (t->kind_kind == OP_EXPR) {		// 运算类型表达式
    				if (t->attr.op == PLUS)			// 加法表达式
    					// 从内存(my_mem)中取出两个孩子的值,进行加法,结果写回内存
    					my_mem[t->addr] = my_mem[t->children[0]->addr] + my_mem[t->children[1]->addr]; 
    				else if (t->attr.op == MINUS)	// 减法的处理类似加法
    					my_mem[t->addr] = my_mem[t->children[0]->addr] - my_mem[t->children[1]->addr];
    				else if (t->attr.op == TIMES)
    					my_mem[t->addr] = my_mem[t->children[0]->addr] * my_mem[t->children[1]->addr];
    				else if (t->attr.op == OVER){
    					if(my_mem[t->children[1]->addr] == 0){
    						cout<<"error: divide by zero"<<endl;
    						my_mem[t->addr] = 0;
    					}else{
    						my_mem[t->addr] = my_mem[t->children[0]->addr] / my_mem[t->children[1]->addr];
    					}
    				}
    				else if (t->attr.op == AND)
    					my_mem[t->addr] = my_mem[t->children[0]->addr] && my_mem[t->children[1]->addr];
    				else if (t->attr.op == OR)
    					my_mem[t->addr] = my_mem[t->children[0]->addr] || my_mem[t->children[1]->addr];
    				else if (t->attr.op == EQ)
    					my_mem[t->addr] = (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    				else if (t->attr.op == GT)
    					my_mem[t->addr] = my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr];
    				else if (t->attr.op == LT)
    					my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]);
    				else if (t->attr.op == NE)
    					my_mem[t->addr] = !(my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    				else if (t->attr.op == GE)
    					my_mem[t->addr] = (my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    				else if (t->attr.op == LE)
    					my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
    				else if(t->attr.op == ASSIGN){
    					my_mem[t->addr] = my_mem[t->children[0]->addr] = my_mem[t->children[1]->addr];
    				}
    			}
    			else if (t->kind_kind == CONST_EXPR)	// 常量表达式,将值(在vali中)保存至分配的内存中
    				my_mem[t->addr] = t->attr.vali;
    			else if(t->kind_kind == ID_EXPR){
    				//do nothing
    			}
    
    		}//EXPR_NODE
    	}
    }
    
    /*
    if:  st_if -> (condition, action)
    while: st_while->(condition, action)
    */
    void basicTest()
    {
    	tree expr;
    	Node *p, *q, *r;
    
    	// 创建结点a
    	p = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(9), Integer);
    	// 创建结点5
    	q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(5), Integer);
    	// 创建减法结点,孩子结点为9和5
    	r = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(MINUS), Integer, p, q);
    	// q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(2), Integer);
    	// p = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(PLUS), Integer, r, q);
    	expr.setRoot(r);
    	expr.get_addr();	// 为(子)表达式(们)分配存储空间
    	expr.execute();	// 执行代码
    }
    void assignTest()
    {
    	/*
    	a = 1
    	output(a)
    
    	两个语句
    	*/
    
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t;
    
    	//构建赋值语句
    	// 创建结点a
    	p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    	// 创建结点1
    	q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(5), Integer);
    	//赋值
    	r = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(ASSIGN), Integer, p, q);
    	s = expr.NewRoot(STMT_NODE, EXPR_STMT, NodeAttr(0), Integer, r);//赋值语句:孩子赋值表达式
    
    
    	//构建输出语句
    	t = expr.NewRoot(STMT_NODE, PRINT_STMT, NodeAttr(), Integer, p);
    
    	//构建组合语句
    	o = expr.NewRoot(STMT_NODE, COMP_STMT, NodeAttr(), Integer, s, t);
    
    	expr.setRoot(o);
    	expr.get_addr();	// 为(子)表达式(们)分配存储空间
    	expr.execute();	// 执行代码
    	cout<<endl;
    }
    
    Node* createOpExpr(tree& expr, Node* p, Node* q, int type)
    {
    	Node* p1;
    	p1 = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(type), Integer, p,q);
    	return p1;
    }
    Node* createId(tree& expr)
    {
    	Node* p;
    	p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    	return p;
    }
    Node* createConst(tree& expr, double value)
    {
    	Node* q2;
    	q2 = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(value), Integer);
    	return q2;
    }
    Node* createSTMT(tree& expr, int type, Node* p1, Node* p2=NULL, Node* p3 = NULL, Node* p4=NULL)
    {
    	Node* r;
    	r = expr.NewRoot(STMT_NODE, type, NodeAttr(), Integer, p1,p2,p3,p4);
    
    	return r;
    }
    Node* createIfStmt(tree& expr,Node* p1, Node* p2, Node* p3 = NULL )
    {
    	return createSTMT(expr, IF_STMT,  p1, p2, p3);
    }
    Node* createWhileStmt(tree& expr, Node* p1, Node* p2)
    {
    	return createSTMT(expr, WHILE_STMT, p1, p2);
    }
    Node* createInputStmt(tree& expr, Node* p)
    {
    	return createSTMT(expr, INPUT_STMT, p);
    }
    Node* createOutStmt(tree& expr, Node* p)
    {
    	return createSTMT(expr, PRINT_STMT, p);
    }
    Node* createExprStmt(tree& expr, Node* p)
    {
    	return expr.NewRoot(STMT_NODE, EXPR_STMT, NodeAttr(0), Integer, p);//xxxx; xxxx为表达式,组合成语句
    }
    Node* createAssignStmt(tree& expr, Node* variable, int value)
    {
    	Node* p = variable;
    	Node *q, *r, *s;
    	q = createConst(expr, value);
    	r = createOpExpr(expr, p,q, ASSIGN);
    
    	return createExprStmt(expr, r);
    }
    Node* createAssignStmt(tree& expr, Node* variable, Node* q)
    {
    	Node* p = variable;
    	Node *r, *s;
    	r = createOpExpr(expr, p,q, ASSIGN);
    
    	return createExprStmt(expr, r);
    }
    Node* createCompStmt(tree& expr, Node* p1, Node* p2=NULL, Node* p3 = NULL, Node* p4=NULL)
    {
    	return createSTMT(expr, COMP_STMT, p1,p2,p3,p4);
    }
    void executeTree(tree& expr, Node* root)
    {
    	expr.setRoot(root);
    	expr.get_addr();	// 为(子)表达式(们)分配存储空间
    	expr.execute();	// 执行代码
    	cout<<endl;
    }
    void inputOutTest()
    {
    	/*
    	input(a)
    	output(a)
    	*/
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t;
    
    	//输入语句
    	// 创建结点a
    	p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    	s = createInputStmt(expr, p);
    	q = createOutStmt(expr, p);
    	o = createCompStmt(expr, s, q);
    
    	executeTree(expr, o);
    }
    void ifTest()
    {
    	/*
    	a = 1
    	if(a>10){
    	  a = 11
    	}else{
    	  a = 7
    	}
    	print(a)
    	*/
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t;
    
    	//输入语句
    	// 创建结点a
    	p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    	s = createAssignStmt(expr, p, 1);//a=1
    
    
    	//if语句
    	q = createOpExpr(expr, p, createConst(expr, 10), GT);//a>10
    	r = createIfStmt(expr, q, createAssignStmt(expr, p, 11), createAssignStmt(expr, p, 7));//if(a>10) a=11 else a=7
    
    	//构建输出语句
    	t = createOutStmt(expr, p);//print(a)
    
    	//构建组合语句
    	o = createCompStmt(expr,s ,r, t );
    
    	executeTree(expr , o);
    }
    void whileTest()
    {
    	/*
    	a =1
    	sum = 0
    	while(a <= 10){
    	   sum = sum + a
    	   a = a+1;
    	}
    	print(sum)
    	*/
    
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t,*p1,*q1;
    	p = createId(expr);//a
    	q = createId(expr);//sum
    	p1 = createAssignStmt(expr,p, 1);//a=1
    	q1 = createAssignStmt(expr, q, 0);//sum=0
    
    	Node *a, *b, *c, *d, *e, *f, *g;
    	a = createOpExpr(expr, p, createConst(expr, 10), LE);//a<=10
    	b = createOpExpr(expr, p, q, PLUS);//sum+a
    	c = createAssignStmt(expr, q, b);//sum = sum+a
    
    	d = createOpExpr(expr, p, createConst(expr, 1), PLUS);
    	e = createAssignStmt(expr, p, d);	//a=a+1
    	f = createCompStmt(expr, c, e); //{sum = sum+a, a= a+1}
    	r = createWhileStmt(expr, a,  f);//while
    
    	t = createOutStmt(expr, q);//print sum
    
    	o = createCompStmt(expr, p1, q1, r, t);
    
    	executeTree(expr, o);
    }
    
    void whileInputTest()
    {
    	/*
    	a = 1
    	sum = 0
    	input(x)
    	while(a <= x){
    	   sum = sum + a
    	   a = a+1;
    	}
    	print(sum)
    	*/
    
    	tree expr;
    	Node *p, *q, *r, *s, *o, *u, *t,*p1,*q1;
    	p = createId(expr);//a
    	q = createId(expr);//sum
    	p1 = createAssignStmt(expr, p, 1);//a=1
    	q1 = createAssignStmt(expr, q, 0);//sum=0
    
    	Node *a, *b, *c, *d, *e, *f, *g;
    	g = createId(expr);//x
    	f = createInputStmt(expr,g);
    	u = createCompStmt(expr, p1, q1, f);//first 3 sentences. because Comp not support more than 4 children.
    
    	a = createOpExpr(expr, p, g, LE);//a<=x, x from input
    	b = createOpExpr(expr, p, q, PLUS);//sum+a
    	c = createAssignStmt(expr, q, b);//sum = sum+a
    
    	d = createOpExpr(expr, p, createConst(expr, 1), PLUS);
    	e = createAssignStmt(expr, p, d);	//a=a+1
    	f = createCompStmt(expr, c, e); //{sum = sum+a, a= a+1}
    	r = createWhileStmt(expr, a,  f);//while
    
    	t = createOutStmt(expr, q);//print sum
    
    	o = createCompStmt(expr, u, r, t);
    
    	executeTree(expr, o);
    }
    int main(int argc, char *argv[])
    {
    	//assignTest();
    	//inputOutTest();
    	//ifTest();
    
    	//whileTest();
    	whileInputTest();
    }
    

    实现效果

    最后放上一个支持用户输入的while循环,它支持用户手动输入一个值x,然后程序会计算1+2+…+x的结果,并打印输出。它对应的c语言代码如下:

    	a = 1
    	sum = 0
    	input(x)
    	while(a <= x){
    	   sum = sum + a
    	   a = a+1;
    	}
    	print(sum)
    

    效果:
    这里写图片描述

    展开全文
  • 上一节,我们把C语言编译成了可以被java虚拟机加载执行的java汇编语言。这节,我们就jvm的基本机制进行深入了解,如果不理解java虚拟机的体系结构,那么我们不可能把C语言转换成能顺利在虚拟机上执行的字节码
  • 开设 “编译原理实验”的主要目的是让学生加深理解编译原理的基本理论、方法、词法分析、语法分析、中间代码的生成直到最后的代码生成,了解编译器原理,从而提高学生分析问、题解决问题的能...
  • 编译原理课程设计报告 设计题目 编译...是计算机专业学生的一门主修课 为了让学生能 够更好地掌握编译原理的基本理论和编译程序构造的基本方法和技巧 融会贯通本课程所学 专业理论知识 提高他们的软件设计能力 特设定
  • 《现代编译原理:C语言描述》全面讲述了现代编译器结构、编译算法和实现方法,是Andrew w.Apple“虎书”——Modern Compiler Implementation——“红、蓝、绿”三序列之一。这三本书内容基本相同。但是使用...
  • 《现代编译原理:C语言描述》全面讲述了现代编译器结构、编译算法和实现方法,是Andrew w.Apple“虎书”——Modern Compiler Implementation——“红、蓝、绿”三序列之一。这三本书内容基本相同。但是使用...
  • 通过设计编制调试一个具体词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词词法分析方法。 编制一个读单词过程,从输入源程序中,识别出各个具有独立...
  • 本程序是本人五邑大学编译原理实验课作业,用了两天时间写得很简易很随便,没有用到有穷自动机状态转换思想,也没有建立真正符号表。仅仅为了应付实验课而写,仅供大家参考和学习,非常不建议大家直接拿去给...
  • C语言编译器,基本上实现了主要功能的C语言语法,词法分析使用状态转移,语法使用LR(1)方法,自动生成ACTION和GOTO转移表。自顶向下语法制导翻译,可以生成各种类型表达式(包括布尔,算术,逻辑等等),...
  • C语言词法分析器--编译原理

    千次阅读 多人点赞 2020-09-21 23:24:06
    这是老师布置的编译原理的实验课任务,课余时间花了近一个星期时间去编写代码(主要是C++太久没有用了,好多函数都不熟悉,查阅了很多资料),这次词法分析没有语法错误判断功能,如果想要增加功能可以在相关函数代码段...
  • 全书分成两部分,第一部分是编译的基础知识,适用于第一门编译原理课程(一个学期);第二部分是高级主题,包括面向对象语言和函数语言、垃圾收集、循环优化、ssA(静态单赋值)形式、循环调度、存储结构优化等,适合于...
  • 全书分成两部分,第一部分是编译的基础知识,适用于第一门编译原理课程(一个学期);第二部分是高级主题,包括面向对象语言和函数语言、垃圾收集、循环优化、ssA(静态单赋值)形式、循环调度、存储结构优化等,适合于...
  • 课程设计目标 1题目实用性 C-语言拥有一个完整语言的基本属性通过编写C-语言的词法分析和语法分析对于理解编译原理的相关理论和知识有很大的作用通过编写C-语言词法和语法分析程序能够对编译原理的相关知识正则...
  • 最近老师要求用C语言做一个词法分析器,要求功能相对完善,能完成基本的词法分析。将输入输出结果以文件形式保存,并用数据测试结果正确性。 编译程序完成词法分析功能,扫描输入字符流,产生用于语法分析...
  • 通过设计编制调试一个具体词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词词法分析方法。 编制一个读单词过程,从输入源程序中,识别出各个具有独立...
  • C语言—切操作都是基于内存 变量和数组都是内存别名 -内存分配由编译器在编译期间决定 -定义数组时候必须指定数组长度 -数组长度是在编译期就必须确定 需求:程序运行过程中,可能需要使用—...
  • 编译原理课程设计第一部分,一个PASCAL语言子集(PL/0)词法分析器设计与实现,包含基本的测试文件。
  • rs编译的c语言实现

    2010-05-22 16:32:22
    介绍RS码基本原理以及编码硬件电路实现,详述了c语言实现的编译吗过程
  • 编译原理课程设计之一用编程语言实现词法分析,用C++实现 注释清楚详细,程序风格良好 /*目前实现功能有: */ /* 0.课程要求词法分析基本功能 */ /* 1.识别用户定义初次定义变量还是已经定义变量还是错误...
  • 从输入源程序中,识别出各个具有独立意义单词,即基本保留字、标识符、常数、运算符、分隔符五大类。 程序输入/输出示例: 如源程序为C语言。输入如下一段: main(){ int a,b; a = 10; b = a + 20; } ...
  • 编译原理词法分析器——C语言实现

    千次阅读 2018-10-31 00:09:52
    实现一个可以识别C语言(子集)词法程序,识别源代码中每个词,并且将结果保存下来。 可识别词包括 头文件:以#include开头过滤为头文件 保留字:“auto”, “break”, “case”, “char”, “const”, ...
  • 霜降将至,气温有所下降,各位亲们注意保暖!不管天气如何,保持每天学习...今天看了一下归并算法,其算法原理如下(文字来源于网络,原理理解就好^_^)归并排序也是一种分割处理式排序算法,它是由Johnyon Neumann...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 581
精华内容 232
关键字:

c语言编译的基本原理

c语言 订阅