APP下载

序列点在C语言表达式求值中的作用

2014-11-27周二强张妍琰

绥化学院学报 2014年8期
关键词:逗号C语言表达式

周二强 张妍琰

(河南城建学院计算机科学与工程学院 河南平顶山 467036)

国内现行C语言教材大多不介绍序列点,这使得读者只能记忆而不是遵循求值规则分析有序列点表达式的求值顺序。以简单的逗号表达式a=3,++a为例,设变量a为整型变量(下面表达式中出现的变量也默认为整型)。“逗号表达式自左向右依次求值”,故先对子表达式a=3求值,再对子表达式++a求值。在实际教学中善于质疑的学生往往会问:表达式中自增操作符++的优先级最高,为何要先求子表达式a=3的值?表达式求值时不是先算高优先级的操作符吗?表达式究竟有没有求值原则呢?序列点作用的缺失不仅使得学生对C语言知识的认知残缺不全,而且也影响了学生自主学习的积极性,不利于创新型人才的培养。

一、序列点的定义及分析

根据C语言标准[1][2][3],序列点就是执行序列中的一些特定点,在这些点上,前面求值的副效应(sideeffect)应彻底完成且其后求值的副效应均未发生。在教材中照搬标准让初学者学习理解序列点这个概念是不明智的,应直接向初学者指出序列点在表达式求值中起的作用。如表达式中有序列点,则求值时序列点左边的操作数要先于其右边的操作数求值。C语言表达式求值的原则为:先考虑序列点,再根据操作符的优先级和结合性。

虽然表达式求值时要首先根据序列点确定求值顺序,但在确定有序列点操作符的操作数时需要结合操作符的优先级和结合性。操作数是指操作符进行操作的操作对象,如表达式3+2中加法操作符左边的操作数为3,右边的操作数为2。在复杂的表达式中,需结合操作符的优先级和结合性来确定某操作符的操作数。对于表达式3+2*5,加法操作符左边的操作数为3,但其右边的操作数不为2,进行加法运算时3显然不可能和2相加。因为加法操作符右边相邻的操作符为乘法操作符,优先级比它高,所以它右边的操作数为2*5(的积)。而乘法操作符左边的操作数为2,右边的操作数为5。对于表达式3+2-5,加法操作符右边相邻的操作符为减法操作符,两者优先级相同,但结合性为左结合,故它右边的操作数为2。

表达式a=0&&++a中逻辑与左边的操作数为0,右边的操作数为子表达式++a,整个表达式为赋值表达式;而表达式(a=0)&&++a中逻辑与左边的操作数为子表达式(a=0),右边的操作数为子表达式++a,整个表达式为逻辑表达式。逻辑与操作符有序列点,因此表达式(a=0)&&++a求值时,虽然自增操作符的优先级最高,但求值时首先考虑序列点,逻辑与操作符左边的操作数(a=0)需先于其右边的操作数++a求值。

二、C语言中部分操作符需要序列点的原因

逗号操作符(,)有序列点。逗号操作符多用于把多条语句变成一条语句,如a=2;和++a;为两条语句,而a=2,++a;是一条语句。语句a=2,++a;执行时,如果逗号操作符没有序列点,子表达式++a就会先执行,即这条语句的执行顺序与上面两条语句的并不相同。基于逗号操作符的作用,逗号操作符只能优先级最低,且含有序列点。

逻辑与操作符(&&)有序列点。C语言中逻辑与操作符实行“短路计算”,即当其左边的操作数值为0即假时,不对右边的操作数求值而直接把0(假)作为求值的最终结果。如果逻辑与操作符没有序列点,表达式3>5&&++a求值时,自增操作符的优先级最高,子表达式++a应先求值。逻辑与操作符左边的操作数3>5求值的结果为0,即假,根据短路计算,作为逻辑与右边的操作数,子表达式++a不会被求值。显然两者矛盾。为了短路计算,逻辑与操作符&&需要序列点。有了序列点之后,求值时序列点左边的操作数将先于其右边的操作数求值,即子表达式3>5先求值,由于短路计算,整个表达式的值为0,即假,且右操作数子表达式++a不会被求值。

逻辑或操作符(||)有序列点。C语言中逻辑或操作符也实行“短路计算”,因此,其有序列点的必要性与逻辑与操作符的相同。

条件操作符?:的问号处?有序列点。条件操作符常用于改写简单的if-else选择结构,如下面的语句:

可用条件操作符改写为a>b?++a:++b;。

如果条件操作符没有序列点,语句a>b?++a:++b;执行时,++a和++b会先于子表达式a>b执行,这样的执行顺序显然与if-else选择结构的不同,因此,条件操作符?:的问号?处有序列点。语句a>b?++a:++b;执行时,问号处?左边的操作数a>b先执行,值为真时,对++a求值,不对++b求值;值为假时,反之。

三、举例分析含有序列点表达式的求值

设整型变量a的值为0。对于表达式'a'||(a=1)&&(a+=2),逻辑或||左边的操作数为'a',右边的操作数为(a=1)&&(a+=2);逻辑与&&左边的操作数为(a=1),右边的操作数为(a+=2)。求值时首先考虑序列点,逻辑或左边的操作数'a'的值非“0”为真,执行短路计算,其右边的操作数不会被求值,因此,整个表达式的值为1,即真。在表达式求值的过程中,变量a的值没有改变。

对于表达式(a=0)&&(a=5)||(a+=1),逻辑与&&左边的操作数为(a=0),右边的操作数为(a=5);逻辑或||左边的操作数为(a=0)&&(a=5),右边的操作数为(a+=1)。求值时首先考虑序列点,逻辑与&&左边的操作数(a=0)先求值,求值时一方面变量a的值会变成0,另一方面这个子表达式的值为0即假,执行短路计算,其右边的操作数(a=5)不会被求值,因此,原表达式变为0||(a+=1)。逻辑或||左边的操作数值为0,即假,不能短路计算,只能再求其右边操作数的值。子表达式(a+=1)求值时一方面变量a的值由0变成1,另一方面这个子表达式的值为1即真,因此,原表达式的值为1,即真。表达式的求值完成后,变量a的值变为1。

特别强调,用赋值表达式作为逻辑操作符的操作数仅仅为了清晰地表明在表达式求值过程中某个子表达式是否被求值,这类表达式的实用性和可读性非常差,实际编程时不能使用这类表达式。

序列点不仅可以使低优先级的操作符先于高优先级的操作符求值,而且也可以影响操作符的结合性。表达式a>b?++a:c>d?++c:++d有两个条件操作符,条件操作符为右结合,因此,原表达式的求值顺序应为(a>b?++a:(c>d?++c:++d))。左边条件操作符的3个操作数分别为子表达式a>b,子表达式++a和子表达式c>d?++c:++d。按照结合性,子表达式(c>d?++c:++d)应先求值,但是,由于左边条件操作符的问号处?有序列点,故子表达式a>b先求值,如果其为真,则只会对子表达式++a求值,而不会再对子表达式c>d?++c:++d求值了。显然,此表达式中条件操作符的右结合只用于确认左边条件操作符的右操作数,而没有使得右边条件操作符先求值。

[1]InternationalOrganization forStandardization.ISO/IEC9899:1999.

[2]InternationalOrganization forStandardization.ISO/IEC9899:1990.

[3]国家技术监督局.GB/T15272-1994程序设计语言C[R].中国标准出版社,1994.

[4]周二强.C语言内涵教程[M].北京:中国铁道出版社,2013.

猜你喜欢

逗号C语言表达式
逗号
逗号
基于Visual Studio Code的C语言程序设计实践教学探索
一个混合核Hilbert型积分不等式及其算子范数表达式
表达式转换及求值探析
浅析C语言运算符及表达式的教学误区
基于C语言的计算机软件编程
高职高专院校C语言程序设计教学改革探索
自傲的逗号
论子函数在C语言数据格式输出中的应用