动态编译

2024-10-13

动态编译(精选3篇)

动态编译 篇1

摘要:由于编译原理课程理论性强、抽象度高、算法体系复杂等特点, 难以取得良好的教学效果。本文基于VS2010的C#设计实现了一个编译算法动态演示系统, 对该课程中的大量算法的实现过程进行了形象、直观的动态演示, 使学生在有限的教学时间里掌握更多的知识, 具有提高教学效果、扩大教学范围和延伸教师功能的作用。

关键词:编译原理,算法,动态演示,C#

《编译原理》是计算机专业的一门重要专业基础课。课程涉及形式语言、有穷自动机等抽象内容, 从词法分析直到代码生成的各个阶段中包含了大量复杂抽象的算法。所以该课程本身逻辑性、理论性和实践性强, 学生普遍存在畏难情绪, 教学效果不理想。作为授课教师, 如何使这门课教学的效果得到改观, 是一个需要研究的课题。近年来, CAI (计算机辅助教学) 日益成为教育领域中的主要手段, 而我院《编译原理》课程辅助教学系统仅仅是利用PowerPoint制作的幻灯片。所以, 计算机辅助教学在该课程中的应用还非常不足。为此, 我们设计了该算法动态演示系统。学生通过该系统能清楚地观察算法的步骤、动作及关联的数据。因其具有形象、直观等特点可以将教师从烦琐的推导和讲解中解脱出来, 使学生在有限的教学时间里理解和掌握更多的知识, 使教学突破了空间和时间的限制, 具有提高教学效果、扩大教学范围和延伸教师功能的作用。

一、系统设计原则

设计本演示系统时遵循以下设计原则:

1. 正确性原则。

本系统作为一种教学资源, 不能出现科学性错误, 绝对不能把错误的概念和理论传授给学生。所以我们将正确性作为第一设计原则。

2. 辅助性原则。

引进CAI技术, 利用动态的教学演示系统能充分刺激感官, 吸引注意力, 有利于激发学生的学习兴趣和主动性的发挥, 从而有效地突破教学重点、难点。但笔者始终认为计算机只能起到辅助教学的作用, 它绝对不能取代教师的作用。因此, 在设计系统过程中应按照教学过程的普遍规律, 坚持教师的主体地位。

3. 适用性原则。

系统的制作必须从教学实际出发, 按照教学的要求提取出重点和难点内容, 考虑这些内容是否适合用计算机进行辅助教学。一味地追求使用辅助教学系统, 会导致辅助系统成为教学的干扰因素。

二、系统的总体结构和功能

编译程序一般包括词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成六个阶段, 除此之外, 还有表格管理和出错处理程序。由于本系统的开发依托于校级教改项目, 最终受益于教师的课堂教学和学生的课下自主学习, 所以结合课程所使用教材, 按教材章节划分模块。主界面中的每章标题都为超链接, 点击后可进入各章界面。每章界面风格保持一致性原则, 左侧为导航链接, 右侧为演示区, 在演示系统构造过程中, 我们注意了系统的简约性。本系统的画面布局突出主体对象, 演示算法所涉及的图表也都尽量简约化。同时, 同一画面色彩数量也不多。在系统中我们大量减少了文字数量, 过多的文字阅读不但容易使人疲劳, 而且还会干扰学生对系统中知识的感知学习。

三、系统的设计与实现

语法分析是编译程序的核心部分。在编译原理中语法分析包括多种分析方法, 其中LR分析法适用范围广但又难于理解, 所以本节以LR分析法为例来说明系统的设计思路及过程。LR分析法根据现行输入符号和分析栈顶的状态来查看分析表, 并根据分析表元素的指示动作采取相应的分析动作, 包括移进、归约、接受、报错四类, 每一类动作涉及的操作各不相同, 其中以归约动作最复杂, 涉及产生式、栈顶若干符号和状态的出栈、归约后的符号入栈、转移到新的状态入栈等子动作。要想清晰演示LR语法分析方法, 算法所涉及的分析动作和相关数据的动态变化过程是演示的重点。因此, 必须在演示界面上显示分析动作及文法产生式、输入串、分析表、分析栈等数据的变化过程。为了控制演示系统的运行, 应考虑各种控制按钮。本系统通过“上一步”和“下一步”按钮可自行控制演示步骤及过程, 当演示过程结束后, 需重新演示一遍时, 不必关闭程序重新运行, 因此设置了“重新开始”按钮进行清屏。LR分析法演示界面如图1所示。本动态演示系统能够逐步演示算法从初始化到结束的每一个步骤, 对于每一种动作, 我们都进行了不同程度的分解, 用颜色或指针等手段突出显示算法的分析动作和关联数据的变化情况。这些动作的分解和涉及的演示操作方法如表1所示。

根据表1所列的动作分类, 在演示过程中按照对应的演示方法逐步演示, 直到程序接受或者报错为止。通过逐步演示算法的分析执行过程, 学生可以清楚地观察到LR分析算法的分析动作及相应数据的动态变化过程, 从而帮助学生深刻理解LR语法分析算法。

本文利用C#语言设计实现了一个编译算法动态演示系统。该演示系统能够通过人机交互的方式, 形象细致地展示编译原理中几个核心算法的基本思想、框架结构与执行步骤, 使学生能够在界面友好的条件下轻松地掌握编译算法中的重点和难点。该动态演示系统是本院《编译原理》课程校级教改项目的成果, 在实际的应用中取得了良好的教学效果。

参考文献

[1]张晶, 陈香凝, 董德义, 李鑫, 赵曦.编译原理[M].哈尔滨:哈尔滨工程大学出版社, 2011.

[2]王强, 冯燕.编译原理算法的形象教学[J].计算机教育, 2010, (3) .

[3]谌志群, 王荣波.基于Flash的编译算法动态演示系统设计[J].计算机时代, 2011, (9) .

动态编译 篇2

关键词:动态数组,Fortran,C/C++,混合编译,调用约定

数组是处理大量数据时的有力助手。在不能够事先确定数组大小的情况下,为避免数据溢出或越界,就要足够大的数组。这种方法使用过度,会迟滞内存操作。故此,理想的数组能够动态确定大小,即程序运行中先向系统申请一个大数组,不使用时再将其内存空间释放掉。而动态数组很好地满足了这一要求。但C语言不支持动态数组,不能动态定义数组元素的数量。如何解决这一矛盾将是本文的着力点。

1 Fortran中的动态数组

Fortran90数组功能强大,执行程序时能够动态分配数组存储空间和实际大小,不使用时再将其内存释放还给系统;支持直接操作数组段、数组整体;支持逐个操作数组元素;支持面向数组操作的函数和构造块[1]。Fortran 90中运用动态数组需要三个步骤:

1)声明动态数组。明确数组的维数,不规定维的上、下界和大小(即延迟形状数组)。如:REAL,DIMENSION(:),ALLOCAT-ABLE::X。

2)给动态数组分配内存。如:ALLOCATE(X(N))。

3)将分配的内存释放掉。如:DEALLOCATE(X)。

2 Fortran与C/C++的混合编译

在32位Windows系统下,同时安装Compaq Visual Fortran 6.x和Microsoft Visual C++6.0,则两者共享一个可视化开发环境(Microsoft Visual Studio 6.0)[1]。Fortran与C/C++任一种语言都可以作为主程序,调用另一种语言编写的例程。但两者在目标例程命名、堆栈管理以及参数传递方面的行为模式不完全一致,要协调其调用约定。

Fortran使用的调用约定有缺省约定(Default)、STDCALL约定和C约定。在Visual Fortran 6.x的编译器下,作用于例程参数和整个外部例程的属性不同,会影响堆栈管理、例程命名、参数传递。

2.1 堆栈管理

Fortran与C/C++间的例程调用是通过堆栈进行的。在缺省约定和STDCALL约定下,堆栈由被调用例程(Colleen)控制。在C约定下,堆栈由调用程序(Caller)控制,所以例程参数驻留在堆栈什么位置、占用多少字节等信息都是调用程序获知,该约定下传递参数的数量是可以变化的,是可选参数[2]。

2.2 例程命名

在编译生成的目标文件(.OBJ)中,标示符诸如全局变量、外部例程、模块等,在整个程序作用域内是独一无二的,共享于各个程序单元[3]。C对字母的大小写敏感,C++增加了特定的修饰,而Fortran忽略字母大小写,使得目标标示符在编译后发生变化。而命名约定,就是保持目标标示符在编译时一致性的命名规则。为保证链接成功,就要协调命名约定。

C++增加的修饰受特定系统影响,如将其去掉,就是在函数中添加C链接选项(extern“C”),则C++与C的命名约定相同。

协调Fortran与C/C++的例程命名分大写、小写和混合三种情况:

1)大写。Fortran是缺省约定,其目标外部例程名统一被转换为大写,那么在C/C++中须采用_stdcall约定,并用大写命名外部函数。

2)小写。Fortran是C约定或STDCALL约定,其目标外部例程名统一被转换为小写,那么在C/C++中须采用缺省约定_cdecl或_stdcall约定,并用小写命名外部函数。

3)大小写混合。C/C++的外部函数名是大小写混合形式,在Fortran中若要保持一致的形式,必须采用ALIAS属性来限定产生的目标例程名。

2.3 参数传递

1)参数传递方式

Fortran参数传递方式取决于调用约定[4]。Default状态下,Fortran为引用传递;C或STDCALL约定状态下,数组参数为引用传递;单个参数为值传递。在调用约定之外,为忽略掉调用约定对参数传递的影响,可以规定参数拥有VALUE和REFERENCE传递属性[5]。在混合编程中,应通过VALUE和REFERENCE属性明确规定参数的传递方式,而不是依赖于调用约定。

C/C++参数传递方式不受调用约定影响。数组参数为引用传递;单个参数为值传递。单个参数可转换为引用方式传递,C中可选指针参数;C++中可选指针参数或引用参数。

2)数据类型的对应

要使Fortran和C/C++的混合编程获得成功,不仅要求对其调用约定、命名约定等进行协调,还要求保持数据类型相互一致。事实上,实型和整型是数值计算过程中经常运用到的,并且在两种语言中其数据类型有着很好的一致性,其对应关系如表1。

3 C中动态数组的实现实例

下面给出一个利用Fortran与C/C++的混合编译在C中实现动态数组的实例。该实例包含以一个外部例程,其功能是利用动态数组实现学生成绩及学生人数的输出。

具体实现过程如下:打开安装好的开发环境,Microsoft Visual C++6.0或Compaq Visual Fortran 6.x中的Developer Studio界面,点击new text file快捷按钮,保存为(.f90)文件,在其中编写Fortran外部例程。

代码如下:

同理,再建立一个(.c)文件编写主程序,在C中调用Fortran例程。

主程序代码如下:

编写好程序代码后,以(.c)文件建立链接并生成活动的工作空间,将Fortran外部例程的文件添加到工作空间中,混合编译即可获得成功,得到正确的运行结果:通过C程序中调用Fortran例程在C中实现动态数组。

4 结论

通过C程序调用Fortran例程的混合编译方法,实现了C语言的动态数组功能,一定程度上弥补了C不支持动态数组的缺憾。此外,其他的优势数组功能可否通过调用的方式增强数组功能的不足,或许是可行的研究方向。

参考文献

[1]周振红.Fortran90/95高级程序设计[M].郑州:黄河水利出版社,2005.

[2]任慧,周振红,张成才.Fortran与C/C++的混合编译[J].计算机工程与设计,2007(9):4096-4098.

[3]毕苏萍,周振红,贺晓慧.Fortran90借用C++函数模板[J].郑州大学学报:工学版,2013(3):64-66.

[4]周振红,王国宾,毕苏萍.Fortran与VB传递单一字符串的几种方式[J].郑州大学学报:工学版,2011(1):116-120.

动态编译 篇3

关键词:C语言,C编译器,——运算,编译

C语言是高校计算机课程的重要部分,同时也是全国计算机等级考试的一部分。目前C语言的学习者越来越多,对C语言的了解也越来越清楚。然而在C语言教学中发现很多学生对其中的自加自减运算符感到迷惑,特别是二者出现前置后置混合使用时。同时,C语言教学中常用的两个编译器,即Turbo C 2.0编译器和Visual C++6.0编译器,对自加自减运算的编译顺序有不同,更加重了学生对此部分内容的不解。因此,本文着重分析上述两种编译器如何对自减运算进行编译,对于自加运算读者可参考文中自减运算进行分析。

1 C语言自加和自减运算

C语言的自减运算符为--,有前置和后置两种用法。前置用法指将运算符放于变量的前面,其功能是在使用变量前,先使变量的值自减1,之后再使用变量的值。后置用法指将运算符放于变量的后面,其功能是先使用变量原来的值参加表达式运算,之后再使变量的值自减1。例如下面的C语句:

很显然,不管是前置用法还是后置用法,对变量本身的影响都是相同的即自减1,受影响的是所在表达式的值。表达式中仅包含单个自减运算时Turbo C 2.0编译器和Visual C++6.0编译器的解释相同,但当表达式中出现不止一个自减运算时,Turbo C 2.0编译器和Visual C++6.0编译器的解释就可能不同。

2 两种编译器中汇编代码的查看

计算机程序如何运行依赖于该语言的编译器,因此通过对编译器的编译结果进行分析是了解编译器如何对运算进行编译的有效方法。

Turbo C 2.0编译器提供有许多工具,其中的Tcc.exe就是一个C语言的编译器,可以将代码编译成目标文件,并能自动调用tlink链接生成可执行文件。Tcc.exe有多种命令行参数,其中使用-S可以生成汇编代码。例如Tcc.exe–S yourfilename.c将生成yourfilename.c源文件的汇编代码文件yourfilename.ASM,进而可对该文件进行汇编代码分析。

Visual C++6.0编译器有汇编窗口,可以以单步运行方式运行程序,之后选择View/Debug Window/Disassembly命令,进入汇编窗口查看对应于源程序的汇编代码。

3 自减运算的单一前置或后置

当出现多个单一前置或多个单一后置时,如何编译又分不同情况。例如下面的程序:

当以Turbo c 2.0编译器编译后运行结果为:12 9 3 6。其解释方式如下:

1)对运算1:因赋值表达式右式的均为自减后置,则先一次性取3个子表达式(a--)的值顺次进行加运算,即((4+4)+4)=12,之后再统一进行a的三次自减操作。则a最终为1,输出结果12。

2)对运算2:格式输出函数的实参为一个加运算表达式,求值时从左向右进行,因为是自减后置,每个子表达式在将a的原值作为子表达式值后直接进行自减操作,所以表达式的值为((4+3)+2)=9。则a最终仍为1,输出结果为9。

3)对运算3:因赋值表达式右式的均为自减前置,则先对a顺次进行3次自减操作变为1,之后再进行3个a的算术加。则a最终为1,输出结果为3。

4)对运算4:格式输出函数的实参为一个加运算表达式,求值时从左向右进行,因为是自减前置,每个子表达式在将a的进行自减操作后将其新值作为子表达式值,所以表达式的值为((3+2)+1)=6。则a最终仍为1,输出结果为9。

当以Visual C++6.0编译器编译后运行结果为:12 12 5 5。其解释方式如下:

1)对运算1:因赋值表达式右式的均为自减后置,则先一次性取3个子表达式(a--)的值顺次进行加运算,即((4+4)+4)=12,之后再统一进行a的三次自减操作。则a最终为1,输出结果12。

2)对运算2:格式输出函数的实参为一个加运算表达式,求值时从左向右进行,而编译器将算术加的优先级视为高于后置自减,所以仍先一次性取3个子表达式(a--)的值顺次进行加运算,即((4+4)+4)=12,之后再统一进行a的三次自减操作。则a最终仍为1,输出结果为12,运算过程与运算1类似。

3)对运算3:因赋值表达式右式的均为自减前置,而编译器将算术加的优先级视为低于前置自减,同时运算从左向右逐次进行,则先对a顺次进行2次自减操作变为2,之后求出前两个子表达式之和为4,之后再进行a的第3次自减变为1,之后再与之前的和相加得5,即((2+2)+1)=5。则a最终为1,输出结果为5。

4)对运算4:格式输出函数的实参为一个加运算表达式,求值时从左向右进行,因为是自减前置,同时运算从左向右逐次进行,则先对a顺次进行2次自减操作变为2,之后求出前两个子表达式之和为4,之后再进行a的第3次自减变为1,之后再与之前的和相加得5,即((2+2)+1)=5。则a最终为1,输出结果为5,运算过程与运算3类似。

4 自减的前置和后置混合使用

当同时出现前置和后置时,如何编译又分不同情况。例如下面的程序:

当以Turbo c 2.0编译器编译后运行结果为:6 7 4 3 3 4 10 19。其解释方式如下:

1)对运算5:对赋值表达式k=(a--)+(--a)+(--a),编译器按照自减前置优先级高于子表达式求值,子表达式求值高于自减后置的顺序进行。所以先进行两次a的自减操作使a的值变为2,之后将a的值分别作为三个子表达式的值进行求和并赋给k,则k的值为6,之后再进行一次a的后置自减使a的值变为1。则a最终为1,输出结果为6。

2)对运算6:格式输出函数的实参为一个加运算表达式,顺次将两两子表达式进行相加,即(((a--)+(--a))+(--a)),对每一个子表达式求值时其中包括的自减操作不论前置、后置均进行完才进行下一个子表达式的求值。所以先求子表达式(a--)的值为4并将a的值自减变为3,之后子表达式(--a)求值时先a自减变为2,再将新值2和上一个子表达式值4相加得6,之后进行第3个子表达式(--a)的求值,a先自减变为1,再将新值1和前两个子表达式之和6相加得7,即对应值为(((4)+(2))+(1))。则a最终为1,输出结果为7。

3)对运算7:此时格式输出函数的输出项列表有四个子表达式,Turbo c 2.0编译器按照从右至左的顺序进行求值,对每一个子表达式求值时其中包括的自减操作不论前置、后置均进行完才进行下一个子表达式的求值。所以先进行(a--)得第4个输出值为4并将a置为3,之后进行(a++)得第3个输出值为3并将a置为4,再之后进行(--a)将a置为3并求得第2个输出值为3,最后进行(++a)将a置为4并求得第1个输出值为4。则最终a为4,输出结果为4 3 3 4。

4)对运算8:此运算和运算9情况类似,两个子表达式((a--)+(--a)+(--a))求值时其中包括的自减操作不论前置、后置均进行完才进行下一个子表达式的求值,而每个子表达式求值和上述运算6一样。所以第2个输出项的对应值为((8)+(6)+(5))并将a置为5,第1个输出项的对应值为((5)+(3)+(2))并将a置为2。则最终a为2,输出结果为10 19。

当以Visual C++6.0编译器编译后运行结果为:8 8 4 3 4 4 14 20。其解释方式如下:

1)对运算5:对赋值表达式的右式,编译器首先从左到右两两子表达式进行加运算,每次运算中前置自减优先级高于子表达式求值,而后置自减则放于整体赋值之后统一进行,即k=(((a--)+(--a))+(--a))。对((a--)+(--a))先进行一次a的前置自减使a的值变为3,之后将a的新值分别作为两个子表达式的值进行加运算得和为6,此时不进行a的后置自减而是继续求下一个子表达式,第3个子表达式(--a)先进行a的前置自减使a变为2并将该值作为子表达式的值与前面两个子表达式之和6相加得8并赋给k,之后再进行a的后置自减使a的值变为1。即k对应值为(((3)+(3))+(2)),a最终为1,输出结果为8。

2)对运算6:格式输出函数的实参为一个加运算表达式,但Visual C++6.0编译器求值时和对运算5的求值相似,即总体按(((a--)+(--a))+(--a))进行。对((a--)+(--a))先进行一次a的前置自减使a的值变为3,之后将a的新值分别作为两个子表达式的值进行加运算得和为6,此时不进行a的后置自减而是继续求下一个子表达式,第3个子表达式(--a)先进行a的前置自减使a变为2并将该值作为子表达式的值与前面两个子表达式之和6相加得8并作为实参值输出,之后再进行a的后置自减使a的值变为1。即实参值为(((3)+(3))+(2)),a最终为1,输出结果为8。

3)对运算7:此时格式输出函数的输出项列表有四个子表达式,Visual C++6.0编译器按照从右至左的顺序进行求值,每次运算中前置自减优先级高于子表达式求值,而后置自减则放于各个输出项的值确定之后统一进行。所以先进行(a--)得第4个输出值为4而a不变,之后进行(a++)得第3个输出值为4而a仍保持原值,再之后进行(--a)将a置为3并求得第2个输出值为3,最后进行(++a)将a置为4并求得第1个输出值为4。则最终a为4,输出结果为4 3 4 4。

4)对运算8:此运算和运算9情况类似,两个子表达式((a--)+(--a)+(--a))求值时每次运算中前置自减优先级高于子表达式求值,而后置自减则放于各个输出项的值确定之后统一进行,而每个子表达式求值和上述运算6一样。所以第2个输出项的对应值为((7)+(7)+(6))并将a置为6,第1个输出项的对应值为((5)+(5)+(4)),之后统一进行两次(a--)置a值为2。则最终a为2,输出结果为1420。

5 结论

通过研究Turbo c 2.0编译器和Visual C++6.0编译器的汇编代码可以看到,两种编译器对自加自减运算的编译是不同的。所以对于C语言的学习者而言,在学习过程中应对所使用的编程环境加以区分,并掌握对这两种编译器的汇编代码的查看方法以帮助自己加深对编译器的理解。

参考文献

[1]刘克成.C语言程序设计[M].北京:中国铁道出版社,2007:42-50.

[2]谭浩强.C程序设计[M].北京:清华大学出版社,1998:30-35.

上一篇:信息交流平台下一篇:交通肇事逃逸行为