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

摘要:

本文开创性地分析了序列点在C语言表达式求值中的作用:序列点左边的操作数要先于其右边的操作数求值。讨论了逗号操作符,、逻辑与操作符&&、逻辑或操作符||和条件操作符?:的问号处需要序列点的原因。举例分析了序列点在表达式求值中的作用。

关键字:序列点 表达式求值 C语言

C语言作为一种主流程序设计语言,许多编程语言如Java、C++、C#都借鉴了它的语法。C语言也是一种很适当的程序设计入门的教学语言,国内大专院校的许多专业都开设了这门课程并且大多将其作为第一门程序设计语言课程,同时C语言也是全国计算机等级考试二级的内容。因此,C语言的教学质量对学生的后续学习和取得等级证书很重要,尤其是对学生在实际工作中更好地了解和使用计算机有非常重要的意义。

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

一、序列点的定义及分析

根据C语言标准[2],序列点就是执行序列中的一些特定点,在这些点上,前面求值的副效应(side effect)应彻底完成且其后求值的副效应均未发生。在教材中照搬标准让初学者学习理解序列点是不明智的,,应直接向初学者指出序列点在表达式求值中起的作用。含有序列点的表达式求值时要保证有序列点的操作符左边的由子表达式构成的操作数先于其右边的操作数求值[3]。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选择结构,如下面的语句

if(a > b)

++a;

else

++b;

可用条件操作符改写为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的值没有改变。

当明知不可挽回,唯一补偿的方法就是怀念,

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

相关文章:

你感兴趣的文章:

标签云: