在C程序中,常量、变量、函数java调用c程序,都是表达式的一种. 对吗??

本文所属图书&>&
本书从编程原理、基本语法、 丰富且循序渐进的例题三个方面以C语言为平台介绍程序设计,旨在开拓学生解决问题的思路,培养学生解决问题的能力。本书的创新之处在于首先通过一个理想厨房的实例类比了计算机系统的...&&
一、判断题
1.两个整型量(包括常量和变量)m和n相除 m/n ,所得的结果是截去了小数部分的整数商。
2. 在调用数学库函数时,可以不包含头文件math.h 。
3.在C89标准中规定:定义可以出现在函数体中的任意位置。
4.表达式中出现的变量,可以是未经过初始化的变量。
5.两个运算量之间的乘号 &*& 有时可以省略不写,有时可以用 &.&号代替&*&号。
6.在赋值语句中,赋值号的左边一定是一个单个变量,不能是常量,也不能是一个函数调用。
7. C语言字符集中的同一个字符,出现在源程序的不同位置,其含义可能不同(考虑圆括号和 % 这两个字符)。
8. C语言程序中,凡是可以出现常量的地方,都可以用一个表达式来替代。
9.赋值语句使得我们既可以通过已知变量求得未知变量的值,又可以通过变量的旧值求得该变量的新值。
10.语句&x=x+1;&是错误的,因为运算符 &=&的两边不相等。
11.没有副作用的表达式永远不会改变变量的值。
12.内存中变量的值可以重复取出任意多次来使用,变量的值都不会变。一旦存入(或输入)一个新值到该变量中,变量的旧值将不复存在。
13.库函数名通常都是由小写英文组成的标识符,而C89中的关键字可以用大写字母构成。
14.单个常量、变量、有返回值的函数调用都是表达式。
15. printf调用时,格式控制串中转换说明的数量要与输出项的数量一样多,类型也要一一匹配。
16.&程序&又可称为&代码&,&源程序&又可称为&源代码&。
二、选择题
1.&以下不是C语言提供的合法关键字的是(  )。
&A. switch&&B. printf&&C. case&&D. default
2.&以下选项中合法的用户标识符是(  )。
&A. long&&&B. _2Test&&C. 3Dmax&D. A.dat
3.&下列各项字符序列中,合法的变量名是(  )。
&A. 2e3&&&B. you&&&C. *y&&D. float
4.&下列可以正确表示字符型常量的是(  )。
&A.'\t'&&&B. &a&&&&C. &\n&&&D. 297
5.&已定义c为字符型变量,则下列赋值语句中正确的是(  )。
&A. c='97'&&B.c=&97&&&C. c='a'&&D. c=&a&
6.&C语言中运算对象必须是整型的运算符是(  )。
&A.& %&&&B. /&&&C. =&&D. *
7.&在C语言程序中,表达式8/5的结果是(  )。
&A. 1.6&&&B.1&&&C. 3&&D. 0
8.&在C语言程序中,表达式5%2的结果是(  )。
&A. 2.5&&&B. 2&&&C. 1&&D. 3
三、问答题
1.设float型变量radius表示圆球的半径,float型变量volumn表示圆球的体积。请指出以下printf函数调用语句中的错误。
1)printf (&圆球的体积是:%f立方米 , 圆球的半径是:%f立方米\n&,& radius );
2)printf (&圆球的体积是:%d立方米\n&,& volumn );
3)printf (&圆球的体积是:%.2f 立方米\n , volumn& );&
4)print (&圆球的体积是:%f 立方米\n& , & volumn );
1)如果要用一次printf()函数调用,依次输出n个变量(或表达式)的值,那么在函数调用的格式串中应当有    个格式符,并且格式符中的转换说明要与输出项的    一一匹配。
2)如果三个待输出的变量的类型依次分别是 int、char、float,那么格式串的形式通常应当是:&* %_* %_%_* && 。其中的星号代表任意多个普通字符或转义字符。
3.已知 age 是int型变量,ch是char型变量,heigh是float 型变量,请用适当的转换说明符 d、f、c填空:
1)scanf(&%__& , &age);&&&
2)scanf(& %__& , &ch);&&&
3)scanf(&%__& , &heigh);&
4.将下列代数式转换成相应的C语言表达式。
1)6y/5x&3a2
2)|yx+lg(x2+1)+3ex+ln(y)|+sin(25&)
5.先阅读,然后通过上机调试,请找出以下程序中的所有错误并改正。
&& 作者: 何勤
&& 编写日期:
&& 功能: 输入圆球的半径,求圆球的体积&
& # include& (stido.h)&&
& #defined& PI& 3.1416 ;&& /*符号常量*/
& Int& mian&& ;
&&&&&& print(&请输入圆球的半径\n&)&&
&&&&&& Float& ,& r& ;&&&&&&&&&&&&
&&&&&& scanf(&%f\n&,& r)&&&&&&
&&&&&& V=(4/3) . PI . r3 ;&&&&&&&&&&&&
&&&&&& Print(&圆球的体积是:%d立方米\n , && &v );
&&&&&& return& 0 ;
6.阅读、编译并运行例题2.1。
1.从键盘输入圆锥体的半径radius和高度height,计算其体积volumn(其中圆周率要求用符号常量表示)。
2.输入年利息、存钱的年数和金额,计算到期本金和利息之和(不计复利)。
3.已知一元二次方程ax2+bx+c=0 的系数a,b,c(a,b,c由键盘数入),并且假设b2&4ac&0,求方程的两个实数根。
4.编写一个程序,让用户输入一个字符,程序显示此字符所对应的ASCII码。要求显示格式如下:
输入的字符是:&&&&&
对应的ASCII码是:
5.编写一个程序,要求用户输入一个ASCII码,程序显示此ASCII码所对应的字符。要求显示格式如下:
输入的ASCII码是:&&&&&
对应的字符是:
6.参照例题2.7,将6位纯数字的明码转变为比较安全的混合密码。要求6位密码由两位大写英文字符、两位小写英文字符和两位数字字符构成。
7.编写学生个人生活小管家程序。输入本月总收入,输入预算支出项,显示输出本月预算情况。支出分为: 就餐费、手机费、日常生活用品费、交友娱乐费。
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'为什么 const 变量不是用常量表达式初始化就不应该在头文件中定义?
[问题点数:20分]
为什么 const 变量不是用常量表达式初始化就不应该在头文件中定义?
[问题点数:20分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2015年1月 C/C++大版内专家分月排行榜第二2012年3月 C/C++大版内专家分月排行榜第二2011年11月 C/C++大版内专家分月排行榜第二2010年6月 C/C++大版内专家分月排行榜第二2010年5月 C/C++大版内专家分月排行榜第二
2011年4月 C/C++大版内专家分月排行榜第三2011年2月 C/C++大版内专家分月排行榜第三2010年8月 C/C++大版内专家分月排行榜第三
2015年1月 C/C++大版内专家分月排行榜第二2012年3月 C/C++大版内专家分月排行榜第二2011年11月 C/C++大版内专家分月排行榜第二2010年6月 C/C++大版内专家分月排行榜第二2010年5月 C/C++大版内专家分月排行榜第二
2011年4月 C/C++大版内专家分月排行榜第三2011年2月 C/C++大版内专家分月排行榜第三2010年8月 C/C++大版内专家分月排行榜第三
2015年1月 C/C++大版内专家分月排行榜第二2012年3月 C/C++大版内专家分月排行榜第二2011年11月 C/C++大版内专家分月排行榜第二2010年6月 C/C++大版内专家分月排行榜第二2010年5月 C/C++大版内专家分月排行榜第二
2011年4月 C/C++大版内专家分月排行榜第三2011年2月 C/C++大版内专家分月排行榜第三2010年8月 C/C++大版内专家分月排行榜第三
2013年 总版技术专家分年内排行榜第三
2012年 总版技术专家分年内排行榜第七
本帖子已过去太久远了,不再提供回复功能。您现在的位置: &
C语言中的常量与变量
  §2.2& 常量与变量
  在程序中不同类型的数据可以以常量形式出现,也可以以变量形式出现。常量是指程序在执行期间值不能发生变化、具有固定值的量。变量则是在程序执行期间其值可以发生变化的量,实际上变量命名了内存中指定的存储单元。
  2.2.1& 直接常量和符号常量
  直接常量就是日常所说的常数(常量)。直接常量从字面上就可以看出它们属于什么类型常量以及具体的值,因此又称为字面常量。符号常量则是在一个程序中指定的用名字代表的常量,从字面上不能直接看出其类型和值。
  例2.4& 分析下列程序运行结果。
  #define& PI& 3.
  #define& R& 2.0
  main( )
  float c,s;
  c=2*PI*R;
  s=PI*R*R;
  printf(“circumference=%f\n”,c);
  printf(“area=%f\n”,s);
  说明:命令行#define PI 3.的作用是在预编译时将程序中凡出现PI的地方全部用3.代替。符号常量的优点是使程序容易理解,可读性好且容易维护。
  2.2.2& 直接常量的书写格式
  (1)整型常量:C语言中的整型常量可以使用十进制数、八进制数、十六进制数等几种形式书写。C语言规定:凡是以 0 开头的数字序列作为八进制处理;凡是以0x开头的数字序列作为十六进制处理;凡是非0且非0x开头的数字序列作为十进制处理;凡是在整型数后加小写字母l或大写字母L作为长整数处理。
  注意:八进制数位的取值范围只能是0~7,十进制数位的取值范围只能是0~9,十六进制数位的取值范围是0~9和A~F(a~f)。
  (2)实型常量:实型常量只能用十进制形式表示,不能用八进制数和十六进制数表示。实型常量可以用小数形式或指数形式表示。例如,3.5,5.0,-.5,7.,2e-5,2.4e3等都是合法的实数。
  注意:小数形式表示实数时必须有小数点,小数点的前或后至少有一个出现数字序列。指数形式表示实数时必须有尾数部分和指数部分,指数部分只能是整数而不能是实数,尾数部分既可以用小数表示也可以用整数表示。实型常量不分单精度型和双精度型,但是可以赋给一个单精度型或双精度型变量。
  (3)字符常量:用一对单撇号括起来的一个字符。例如,‘a’,‘A’,‘#’等是字符型常量,而‘ABC’不是字符型常量。
  注意:一对单撇号只是字符与其它部分的分隔符,或者说是字符常量的定界符,不是字符常量的一部分,当输出一个字符常量时不输出此撇号。
  (4)转义字符:C语言中对一些特殊的字符(例如,控制字符)用转义字符来表示。C语言规定:(1)用反斜杠开头后面跟一个字母代表一个控制字符;(2)用两个反斜杠代表一个反斜杠字符,用反斜杠加单撇号代表单撇号字符;(3)用反斜杠加1~3个八进制数位代表ASCII码为该八进制数的字符;(4)用反斜杠加小写字母x和1~2个十六进制数位代表ASCII码为该十六进制数的字符。
  (5)字符串常量:用一对双引号括起来的零个或多个字符序列称为字符串常量。字符串常量以双撇号为定界符,但双撇号并不属于字符串。例如,“Hello”是字符串常量,但‘AB’既不是字符常量也不是字符串常量。字符串常量在机器内存储时,系统自动在字符串的末尾添加一个“字符串结束标志”(转义字符‘\0’),故字符串常量“Hello”实际在内存中占有6个字节的存储单元而不是5个字节的存储单元。
  2.2.3& 变量和对变量的赋值
  (1)变量概念:数据被存储在一定的存储空间中。数据的加工就是在它们所在的存储区与运算器之间的传递过程中实现的。高级语言程序中数据及其存储空间被抽象为变量。每个变量都有一个名字,这个名字称为变量名。变量名代表了某个存储空间(左值)及其存储的数据(右值)。这个存储空间中存储的数据,称为该变量的值。这个存储空间的首地址,称为该变量的地址。
  (2)变量赋值:把从运算器向变量所代表的存储单元传送数据的操作称为赋值。赋值操作的语法格式为:
  变量=表达式
  注意:
  (1)赋值符号和等于号不同。
  (2)赋值运算的方向从右向左,即先计算赋值号右边表达式的值,然后再将计算结果赋给左边的变量。
  (3)C语言中把用赋值号连接变量和表达式的式子称为赋值表达式。赋值表达式的值就是被赋值后左边变量的值。可以将一个赋值表达式的值再赋给另一个变量。例如,b=a=3。
  (4)赋值号的左边只能是变量或相当于变量的数组元素。
  2.2.4& 变量的声明
  (1)声明的定义:C语言中规定在程序中用到的每一个变量都要声明它们的名称及属于哪一种数据类型。变量声明的语法格式为:
  类型标识符 变量名1,变量名2,变量名3,…;
  (2)数据类型的意义:不同类型的数据在内存中占据不同长度的存储单元,而且采用不同的存储方式;一种数据类型对应着一个值的范围;一种数据类型对应着一组允许的操作。简面言之,在引用变量之前先声明指定变量的类型,这样在编译时系统就会根据指定的类型分配给该变量一定的存储空间,并决定数据的存储方式和允许操作的方式。
  (3)变量的初始化:C语言允许在声明变量时给该变量赋一个初值。这种给变量设置初值的工作称为初始化。例如:
  char a=‘A’;
  int b=1320,c=3000;
  注意:如果没有给变量进行初始化并不意味着该变量中没有数值,而只表明该变量中尚未定义特定的值。
  2.2.5& 标识符
  标识符是给程序中的实体(变量、常量、函数、数组、结构体和文件等)所起的名字。C语言中的标识符命名规则为:以字母或下划线开头,由字母、数字或下划线组成的字符序列。C语言中的标识符分为以下三类:
  (1)保留关键字:C语言中规定了一类标识符,它们在程序中代表着特定的含义,不允许用户另作它用。例如int、float、while、else等。
  (2)标准标识符:C语言中规定了一类标识符,它们被用作库函数名和预编译命令名等。虽然C语言允许用户另着它用,但是这将使这类标识符失去系统规定的原意而引起不必要的误解,因此建议用户不要将这些标准标识符另作它用。例如,sin、printf、scanf等。
  (3)用户标识符:由用户自己根据需要定义的标识符。用给户标识符命名时除了要遵循标识符的命名规则外,还应该考虑到标识符“见名知义”的原则。
  注意:C语言区分大小写字母,通常用小写字母表示变量、函数、数组和文件等名而用大写字母表示符号常量名,以便增强程序的可读性。
了解更多相关知识可进入学习IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
本文主要与您探讨了表达式的一般模型及相关概念,并以一种循序渐进的方式阐述了一种表达式解析与计算方法,并提供了一个支持可扩展的表达式解析与计算的设计实例。
, 软件工程师,
王建光为 IBM 一名软件工程师,目前正参与开发 IBM System Director 产品。他还曾经领导过大型连锁系统项目,开发过 C++、Java 产品。目前对软件架构、软件设计模式等领域非常感兴趣。
, 软件工程师,
钱韬,目前任职于 IBM 中国系统技术实验室,主要参与 IBM Systems Director 产品的开发, 熟悉 IBM 的各种 x86 架构的服务器及其管理协议与技术。
概述在应用软件开发领域,对表达式计算的应用有非常广泛的应用。例如,在报表开发中,经常为用户提供公式输入功能,从而实现更灵活的报表汇总;工作流应用软件中,经常利用逻辑条件进行动态配置,从而提供更加灵活的流程配置;另外,在某些 UI 开发中,需要通过某个属性的表达式计算结果来动态控制 UI 组件的显示。所有这些应用都可以归结为一个通用模型,即表达式的解析以及计算。本文旨在提供一种可扩展的表达式解析及其计算方法。表达式解析的一般条件及因素本文所讲的表达式是一种以一定的运算规则组合所表达的字符串;另外,通过解析表达式字符串并以其代表的运算规则可以得到一个结果。表达式解析一般需要满足下列条件:支持的操作符集合操作符的优先级操作符所代表的操作规则集合支持的分隔符集合以及分隔符所代表的意义支持的数据类型集合语法约束,如命名规则、分割符所代表的语法规则等表达式解析除了以上必须满足的条件之外,在有些表达式环境中,可能还支持函数、变量。结合本文所要解决的问题,如下列出可选的条件:支持的内部函数集合支持的内部全局变量支持函数定制支持自定义变量支持函数以及操作符重载以上最后三个可选条件,是一个表达式解析引擎的可扩展支持需要满足的条件。再谈编译原理一般的编译过程主要包括词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成,如下图所示。其中,词法分析主要任务是输入源程序,对构成源程序的程序进行扫描和分解,识别出一个一个的单词。单词是语言中具有独立意义的最基本结构。一般这些单词包括程序语言所支持的保留字、数据类型、操作符、分隔符以及程序定义的标识符、常量等。语法分析的主要任务是在词法分析的基础上,根据语言的语法规则把单词序列组合成各类语法单位(一般表示成语法树)。语义分析的主要任务是进一步分析语法结构正确的程序是否符合源程序的上下文约束、运算相容性等语义约束规则。图 1. 编译一般过程上述只是非常简要的介绍了一般的编译原理知识,详细知识需要参考编译原理相关的书籍和文档。对于本文所讲的表达式解析,笔者认为其与程序编译具有本质上的相似,只是在问题的复杂性上会简单的很多。表达式解析同样需要首先把输入的表达式字符串分解成一个一个的单词,然后把单词序列组合成语法单位,最后依据表达式的语言环境所定义的语义约束对这些语法单位进行分析计算。因此,我们可以程序编译的基本方法有选择的运用到表达式解析上。中缀表达式、前缀表达式、后缀表达式前缀表达式(又叫波兰式)是一种不含括号的 ,并且将 放在 前面的表达式。对于一个前缀表达式的求值而言,所有的计算按运算符出现的顺序,严格从左向右进行。后缀表达式(又叫逆波兰式)也是一种不含括号的 ,并且 放在操作数的后。对于一个后缀表达式的求值而言,所有的计算按运算符出现的顺序,严格从左向右进行。中缀表达式是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间。中缀表达式不容易被计算机解析,但它符合人们的普遍用法。与前缀或后缀记法不同的是,中缀记法中括号是必需的。计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。前缀表达式和后缀表达式是一种十分有用的表达式,它可以依靠简单的操作就能得到运算结果的表达式。通常解析程序一般都会将中缀表达式转换为前缀表达式或后缀表达式,方便表达式计算。表达式树前面讲过,表达式是一种以一定的运算规则组合所表达的字符串。每一个表达式的基本组成单位都是由一个操作符和操作数组成的操作单位,操作数可以是一个或者多个。所以一个表达式基本操作单位可以表示为一棵树,下图所示为一个N元操作符的树表示,该操作符代表的运算需要N个操作数:图 2. N元操作符的树表示每个基本操作单位可以表示为一棵树,每个操作单位的运算结果为其它操作符的一个操作数,从而一个含有多个操作符的表达式可以表示成一棵更大的树,其每一棵子树皆为一个子表达式。如表达式 a*b+c/d 可以表示为:图 3. 表达式 a*b+c/d 的树表示从编译原理的知识可以得出这样一个结论,一个表达式可能推导出多棵树,呈现出二义性。因此,需要通过对表达式的语言环境增加语义约束规则,消除二义性,保证从表达式只能推导出唯一的表达式树。通常的语义约束规则包括设定操作符的优先级、括号的使用等。表达式解析引擎的基本结构设计图 4. 解析引擎的基本结构设计()如
所示,展示了一个表达式解析引擎的基本结构设计。整个表达式解析引擎内部结构主要包括:表达式解析引擎的语言环境定义、操作符处理器管理、表管理器、表达式解析器、表达式校验器、表达式解析异常定义等。本文后续部分将详细解释。表达式解析引擎实现说明定义表达式解析引擎的语言环境对于表达式解析,首先必须定义表达式所存在的语言环境。主要包括表达式所支持的数据操作、数据类型、合法的操作符集合、保留字、分隔符等。另外还包括内部自定义并可供表达式引用的全局变量、函数等。所有这些语言环境因素都通过表格管理器进行统一管理,如
所示 TableManger 类。表格管理器主要用于符号管理,主要管理如下四类符号数据:操作符管理操作符管理主要管理表达式所支持的所有操作符,包括内置操作符以及用户自定义的操作符。其中,用户自定义的操作符属于表达式解析引擎中可扩展特性,需要注册到表格管理器,以便表达式引用。操作符具备一般的符号特性,包括操作符标示字符串,操作数以及操作数个数。另外,不同的操作符具有不同的操作优先级。语法保留字管理语法保留字管理主要存储表达式所支持的各种关键字、分隔符等,属于表达式构成的一部分。如"("、")"、","、"const"、"var"等。函数管理函数管理主要管理表达式所支持的所有函数,包括内置函数以及用户自定义的函数。其中,用户自定义的函数属于表达式解析引擎中可扩展特性,需要注册到表格管理器,以便表达式引用。变量管理变量管理主要管理表达式解析引擎内置的全局变量以及用户自定义的变量。其中,用户自定义的变量属于表达式解析引擎中可扩展特性,需要注册到表格管理器,以便表达式引用。对于表格管理中的可扩展特性将在本文稍后部分讲解。下面给出符号定义结构示例:图 5. 符号定义结构()Symbol 类定义了表格管理中一般性符号,包括标识字符串 identifier 和转义字符串 escape 两个属性。OperationSymbol 类定义了一般性的操作符号,包括操作数个数 dimension 以及操作数集合 operands。前面讲过,表达式中任何操作都可以抽象归纳为操作符以及零或多个操作数两部分。本文涉及的操作主要包括基本操作符,函数以及取值符所代表的操作。当然,操作数包括数据类型、数据值等属性,用 Variable 类型的对象表示。需要注意的是,任何一个表达式一般都是一个字符串。所以,表达式解析过程需要正确解析字符串表达式中各个操作数的数据类型。Operator 类定义了表达式解析引擎中基本的操作符号,如 +、—、*、/、&&、|| 等。Operator 类包括操作符优先级 priority 以及操作处理器 handler。Function 类代表函数,包括函数实现类 clazz 属性以及用于判断函数是否静态的 isStatic 属性。ValueOperator 类定义了表达式中的取值操作符。因为一个解析引擎通常仅仅只有一个取值操作符,所以该类属于一个单例模型,包括一个 ValueOperator 对象实例属性以及取值操作符处理器 handler。本节阐述了表格管理器的内容,以下
所示给出一个表格管理器示例,简要列出表格管理器属性及操作。清单 1. 表格管理器 public class TableManager {
// 管理 / 存储表达式语言环境涉及的操作符,包括解析引擎保留操作符以及用户注册的自定义操作符
private static Hashtable&String, Operator&
tblOperator = new Hashtable&String, Operator&();
// 管理 / 存储表达式语言环境涉及的语法关键字
private static Hashtable&String, Symbol&
tblSyntaxKey = new Hashtable&String, Symbol&();
// 管理 / 存储表达式语言环境定义的函数,包括解析引擎内部函数以及用户注册的自定义函数
private static Hashtable&String, Function&
tblFunction = new Hashtable&String, Function&();
// 管理 / 存储表达式语言环境定义的变量,包括解析引擎内部全局变量以及用户注册的自定义变量
private static Hashtable&String, Variable&
tblGlobalVar = new Hashtable&String, Variable&();
// 标示符最大长度,方便表达式解析过程中的回朔处理
private static int MAX_IDENTIFIER_LEN
// 操作符是否可被重写处理器
private static boolean isOverridable =
// 根据标示符号字符串取得操作符
public static Operator getOperator(String identifier) {
// 省略代码 ....
// 根据标示符号字符串取得函数
public static Function getFunction(String identifier) {
// 省略代码 ....
// 根据标示符号字符串取得标示符
public static Symbol getIdentifier(String identifier) {
// 省略代码 ....
// 注册操作符
public static boolean regOperator(Operator oper) {
// 省略代码 ....
// 注册语法关键字
public static boolean regSyntaxKeys(Symbol identifier) {
// 省略代码 ....
// 注册函数
public static boolean regFunction(Function func) {
// 省略代码 ....
// 判断是否存在某标示符
public static boolean existIdentifier(String identifier) {
// 省略代码 ....
// 判断是否存在某操作符
public static boolean existOperator(String identifier) {
// 省略代码 ....
// 判断是否存在某函数
public static boolean existFunc(String identifier) {
// 省略代码 ....
// 取得操作符优先级
public static int getPriority(String identifier) {
// 省略代码 ....
// 取得操作符定义的操作数个数
public static int getDimension(String identifier) {
// 省略代码 ....
// 取得最大标示符长度
public static int getMaxIdentifierLen() {
return MAX_IDENTIFIER_LEN;
// 取得单词类型
public static TokenType getTokenType(String token) {
// 省略代码 ....
// 判断是否为取值操作符
public static boolean isValueOperator(String token) {
// 省略代码 ....
// 根据变量名取得已注册的变量
public static Variable getVariable(String varName) {
// 省略代码 ....
// 判断是否存在某变量
public static boolean existVariable(String varName) {
// 省略代码 ....
// 注册变量
public static boolean regVariable(Variable var) {
// 省略代码 ....
}操作符处理器以及可扩展的问题在表达式解析过程中,除了正确解析出各种操作符以及操作数,还需要能依据这些操作符所代表的操作语义正确地进行数据的操作处理。 在本文的"表达式解析的一般条件及因素"部分,提出了三个可选条件作为解析引擎的扩展支持,从而增强解析引擎的功能。本文所提出的设计方案主要采用 Bridge 设计模式和反射模式实现操作符处理以及扩展支持。图 6. 操作符处理器及可扩展处理结构()如
所示,提供了一个操作符处理器及可扩展处理的设计结构。组成表达式的操作单元主要包括基本操作符运算、变量取值、函数调用等。在
所示的结构中,表格管理器 TableManager 主要用于管理各种操作符、函数、变量等并以表的形式进行存储。至于表的实现,具体有很多种方法,如利用哈希表等。在基本操作符 Operator 和取值操作符 ValueOperator 的定义结构中,都定向聚集关联了一个操作处理器对象 handler。Operator 类的操作处理器类型为 IExpressionHandler 接口类型 , ValueOperator 类的操作处理器类型为 IValueOperatorHandler 接口类型。通过该设计方法,除了表达式解析引擎内部实现的缺省操作处理器,用户同样可以通过实现 IExpressionHandler 接口和 IValueOperatorHandler 接口,向表管理器 TableManager 中注册个性化业务相关的操作符处理以及取值处理。具体如下:操作符处理:用户可以自定义操作符或者进行操作符重载。首先,用户通过实现 IExpressionHandler 接口实现用户第三方操作符处理;然后,定义或重载操作符对象,并且引用指向用户实现的第三方操作符处理器; 最后,通过 TableManager 提供的注册方法注册该操作符。取值符处理:实现解析引擎内部的全局变量取值以及第三方业务相关的取值处理。对于全局变量的取值,首先需要注册自定义的全局变量,从而保证在表达式的计算过程中能通过查找表管理器 TableManager 中的全局变量表来获得变量的值。对于第三方业务相关的取值处理,首先需要实现 IValueOperatorHandler 接口来实现第三方取值处理,然后通过表管理器 TableManager 注册第三方处理器到取值符中(前面讲过,解析引擎通常仅仅只有一个取值操作符,所以单独注册取值处理器到取值操作符中)。函数处理:实现解析引擎内部函数调用以及第三方自定义函数调用。函数处理主要通过在运行时状态下利用反射方法动态调用。表达式可利用的每个函数都需要通过表管理器 TableManager 注册到函数表中。在表达式解析计算过程中,当遇到函数调用时,函数处理器通过查询函数表得到函数相关信息,然后利用这些函数信息反射调用函数,从而得到函数的执行结果。如
所示,为一个加法处理器代码示例。该加法处理器能处理整型数字、浮点数以及字符串相加。清单 2. 加法 + 操作符处理器 /**
* 加法操作符处理器,支持数值相加以及字符串相加
* @author Wang Jianguang
public class PlusOperatorHandler implements IExpressionHandler {
* 加法操作符处理器方法
* @param oper : 操作符号
* @return : Variable 类型的操作符处理结果
public Variable operate(OperationSymbol oper) {
Variable res =
if(!validate(oper)) {
String identifier = oper.getIdentifier();
if(!identifier.equals("+")) {
Variable[] operands = oper.getOperands();
if(operands.length != 2) {
boolean isStrOp = containStrOperand(operands);
Object val0 = operands[0].getValue();
Object val1 = operands[1].getValue();
if(isStrOp) {
res = new Variable(DataType.STRING);
res.setValue(val0.toString() + val1.toString());
boolean isFloatResult = operands[0].getType() == DataType.FLOAT||
operands[1].getType() == DataType.FLOAT;
if(isFloatResult) {
float fvalue = Float.parseFloat(val0.toString())
+ Float.parseFloat(val1.toString());
res = new Variable(DataType.FLOAT);
res.setValue(fvalue);
int ivalue = Integer.parseInt(val0.toString())+
Integer.parseInt(val1.toString());
res = new Variable(DataType.INT);
res.setValue(ivalue);
}表达式解析及计算方法要正确解析表达式,首先必须通过词法分析能识别出表达式中一个一个的单词,这些单词包括基本操作符、关键字、分隔符和操作数,还可能包括函数、变量等。一般给定一个待计算的表达式,通常是符合某种语言环境所提供的语法结构;往往这种语法并不一定适合计算机内部计算,或者说,对计算机计算而言比较复杂。所有,对于表达式的分析与计算,通常需要把表达式转换为可以依靠简单的操作就能得到运算结果的表达式。前面讲过,通常解析程序一般都会将中缀表达式转换为前缀表达式或后缀表达式,方便表达式计算。本文正是将表达式转换为后缀表达式来进行分析与计算的。图 7. 表达式解析与计算过程如
所示,展示了表达式的解析与计算过程。下面就该过程的每部分进行简要说明。词法分析:识别出表达式中合法的单词,一般利用回朔算法进行分析,并利用表管理器来辅助识别。生成后缀表达式栈:将表达式(一般为中缀表达式)转换为后缀表达式,并以栈结构进行存储,方便后续构造表达式树。清单 3. 生成后缀表达式栈 /**
* 构造后缀表达式栈。通过词法分析,获得各种类别的单词,从而依据语法规则构造
* 后缀表达式栈
* @param expr 输入表达式
* @return 由各个单词构成的表达式栈
public Stack&String& buildPostExpressionStack(String expr) {
Stack&String& retStack = new Stack&String&();
Stack&String& stack = new Stack&String&();
stack.push("ENDFLAG");
int iPointer = 0;
while(iPointer & expr.length()) {
// 词法分析,得到单词
String token = this.getToken(expr, iPointer);
int step = token.length();
if(token.equals("(")) {
stack.push(token);
} else if(TableManager.existOperator(token)) {
// 分析操作符
String lastToken = stack.lastElement();
while(TableManager.existFunc(lastToken)
|| TableManager.isValueOperator(token)
|| TableManager.getPriority(lastToken)
&= TableManager.getPriority(token)) {
retStack.push(stack.pop());
lastToken = stack.lastElement();
stack.push(token);
} else if(TableManager.existFunc(token) ||
TableManager.isValueOperator(token)) {
// 分析函数或取值符号
stack.push(token);
} else if(token.equals(",")) {
while(!stack.lastElement().equals("(")){
retStack.push(stack.pop());
} else if(token.equals(")")) {
while(!stack.lastElement().equals("(")) {
retStack.push(stack.pop());
stack.pop();
String lastToken = stack.lastElement();
if(TableManager.existFunc(lastToken)) {
retStack.push(stack.pop());
retStack.push(token);
iPointer +=
while(!stack.lastElement().equals("ENDFLAG")) {
retStack.push(stack.pop());
adjust(retStack);
return retS
}生成表达式树:前面讲过,一个含有多个操作符的表达式可以表示成一棵树。此过程正是利用此结论来构造一棵表达式树,方便后续的分析与计算结构。树的每个节点都保存每个操作符或操作数的相关信息。以树的结构存储表达式还方便扩展分析处理。如
所示为生成表达式树代码。清单 4. 生成表达式树 /**
* 构造表达式树。首先通过词法分析构造表达式栈,然后构造表达式树。
* @param expr 输入表达式
* @return 表达式树
public TreeNode buildTree(String expr) {
Stack&String& postExprStack = buildPostExpressionStack(expr);
Stack&String& tmpStack = reverseStack(postExprStack);
Stack&TreeNode& nodeStack = new Stack&TreeNode&();
while(!tmpStack.isEmpty()) {
String token = tmpStack.pop();
TreeNode node = new TreeNode(token);
TokenType nodeType = TableManager.getTokenType(token);
node.setNodeType(nodeType);
if(nodeType == TokenType.FUNCTION
|| nodeType == TokenType.OPERATOR
|| nodeType == TokenType.VARIABLE) {
int dimension = TableManager.getDimension(token);
TreeNode[] children = new TreeNode[dimension];
for(int j = dimension-1; j &= 0; j--){
children[j] = nodeStack.pop();
node.setChildren(children);
nodeStack.push(node);
nodeStack.push(node);
TreeNode root = nodeStack.pop();
}分析与计算:该过程结合表管理器,通过递归分析与计算表达式树,得到表达式计算结果。清单 5. 表达式分析与计算 /**
* 计算表达式。 通过递归解析计算表达式树得到计算结果。
* @param expr 输入表达式
* @return 计算结果
public Variable computeExpression(String expr) {
Variable value =
if(expr == null) {
// 构造表达式树
TreeNode tree = buildTree(expr);
if(tree == null) {
// 解析计算表达式树
value = computeSubTree(tree);
* 解析计算表达式子树
* @param node 子树根节点
* @return 子树计算结果
public Variable computeSubTree(TreeNode node) {
Variable varRet =
if(node == null){
String value = node.getValue();
if(!TableManager.existOperator(value)
&& !TableManager.existFunc(value)
&& !TableManager.isValueOperator(value)){
return ParserUtil.getValue(value);
TreeNode[] children = node.getChildren();
if(children == null || children.length == 0) {
return ParserUtil.getValue(value);
Variable[] operands = new Variable[children.length];
for(int i = 0; i & children. i++) {
operands[i] = computeSubTree(children[i]);
OperationSymbol op = getOperationSymbol(node, value);
if(op != null) {
op.setOperands(operands);
varRet = op.operate();
return varR
}表达式校验器:在表达式分析与计算的每一个过程中,都需要利用表达式校验器来保证其数据的合法性,如操作符是否合法、语法是否正确、分析得到的操作数数据类型是否正确、函数的参数类型是否正确等等。每当遇到校验失败的情况,都会抛出各种类型的异常,并中止解析过程,表示解析失败。下面给出一个表达式解析引擎应用示例,如
所示。清单 6. 表达式解析引擎应用 public class ExpressionParserTester {
* 初始化:注册用户自定义的函数以及变量到表达式解析引擎
public static void init() {
TableManager.regFunction(
new Function("test",
"com.gavin.parser.expression.test.Test",
new DataType[]{DataType.INT,DataType.INT,DataType.INT},
DataType.INT,
TableManager.regFunction(
new Function("test2",
"com.gavin.parser.expression.test.Test",
new DataType[]{DataType.STRING},
DataType.INT,
TableManager.regVariable(new Variable(DataType.INT, "GB_VAR", 31));
public static void main(String[] args) {
String expr =
"1+2*test($GB_VAR,test((1+3)+4*(5+6 ),2,(2+3)%2),
test(-10,10,11*(1+test(3,4,5))))+test2('300')*3";
TExprParser parser = TExprParser.getInstance();
// 测试 1: 建立后缀表达并打印
String postExp = parser.buildPostExpression(expr);
System.out.println(postExp);
// 测试 2: 解析以及计算表达式并打印结果
String value = parser.parse(expr);
System.out.println(value);
public static int test(int a, int b, int c) {
return a+b+c;
public static int test2(String param) {
return Integer.valueOf(param).intValue();
}总结本文以一种循序渐进的方式阐述了一种表达式解析与计算方法,并提供了一个支持可扩展的表达式解析与计算的设计实例。首先本文提出定义了表达式的一般模型及相关概念;然后从引入编译原理一般过程开始,逐步介绍了表达式的三种类型及表达式树的概念。最后提供一个设计实例来介绍表达式解析与计算的方法与步骤。笔者希望籍以本文为读者抛砖引玉,在表达式解析方面能提供些许指引。
参考资料 :Nikola Stepan,文章分析了 W3Eval 与经典表达式算法所不同的求值步骤,并通过展示代码片段详细地介绍其中的算法逻辑。
:介绍了如何基于 Windows 平台进行软件开发。
:孙鸣,邓辉,文章介绍了如何基于 DSL 设计思想来构建一个字符串解析器,并提供了一个用该解析器识别 H.248 协议中 NAME 语法的具体实现。
:杨博文,应乐年,杨雯雯,文章从 String 在 JVM 的存储结构出发,介绍了 Java String API 使用中的性能问题以及对应的解决方法。
更多编译原理知识可参考 Alfred V.Aho, Monica S.Lam, Ravi Sethi, Jeffrey D.Ullman 编写的《编译原理》一书。:这里有数百篇关于 Java 编程各个方面的文章。
加入 :查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=933840ArticleTitle=一种可扩展的表达式解析及计算方法publish-date=

我要回帖

更多关于 java调用c程序 的文章

 

随机推荐