首页 > 范文大全 > 正文

C语言教学中关于自加运算符理解的误区

开篇:润墨网以专业的文秘视角,为您筛选了一篇C语言教学中关于自加运算符理解的误区范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!

摘要:C语言教学过程中普遍存在一个对C语言中自加运算符的错误认识,根据C99规范对该错误进行详细分析,指出后缀加运算符与前缀加运算符的本质区别,解释C语言中令人颇难理解的一个特性。

关键词:C语言;自加运算;后缀加;前缀加;优先级

文章编号:1672-5913(2013)03-0026-03

中图分类号:G642

随着计算机教育的普及,C语言已经成为各所高校理工科专业的必修课程。由于C语言本身是为熟练程序员准备的程序设计语言,使用非常灵活,但同时又极容易出错,因此对教学者提出了更高的要求。

笔者在10多年的教学过程中,发现自己对C语言的某些知识理解是错误的;在与同行交流时,也发现某些同行由于缺乏实际的编程经验,犯下一些知识性的错误。目前使用c语言容易犯的错误已高达100多种,其中有一个对自加运算符的理解错误最为普遍,流传甚广,却至今无人明确提出,笔者拟对此作出澄清。

1 关于后缀自加的一个主流观点

自加运算符(++)是C语言中使用最普遍的一个运算符(由于自减运算符存在的问题与自加完全相同,下面只谈自加运算符),它分为前缀加和后缀加两种。其中前缀加的运算规则相对比较简单,一般均可正确理解。但后缀加的运算规则理解起来要困难得多,笔者发现不少同仁均在此处犯错,且极不容易发现。

在我国高校中使用最广泛的C语言教材是谭浩强先生的《C语言程序设计》,至2009年,该书已经再版3次,发行量超过1000万册,可谓影响深远。该书在介绍自加运算符时,先是如此说明:

++i,--i(在使用i之前,先使i的值加或减1)

i++,i--(在使用i之后,使i的值加或减1)

并补充解释到:“但++i和i++的不同之处在于++i是先执行i=i+1后,再使用i的值;而i++是先使用i的值后,再执行i=i+1”。随后举了一个程序例子:

i=3:

j=++i;//j的值为4

i=3;

j=i++;//j的值为3,然后i变为4

在程序之后,作者又补充到:“注意(i++)是先用i的原值进行运算以后,再对i加1”。从上述描述不难看出,谭浩强先生对于“j=i++”执行的过程可以理解为下面两步操作:

ij

i+1i

从中可以看出,后缀加的运算优先级比赋值号的优先级更低。该观点在随后的两个版本中虽然描述文字有所出入,但基本思想一直保留。谭浩强先生的这一观点受到普遍认同,笔者随机找到10余种C语言程序设计书籍,均同意这一观点。虽然各书所举的例子不尽相同,但都能印证这一解释的正确性。于是这一解释成为目前C语言教学中的主流观点,笔者曾不止一次听到在课堂教学中老师将这一解释介绍给学生,并将其简单总结为:“前缀加是在其他操作之前加1,后缀加是在赋值操作之后加1”。

2 无法解释的矛盾

上述观点在仅有算术运算的情况下能够正确解释程序运行的结果,再加上谭浩强先生的权威,因此成为了主流观点。但实际上,这一观点有一个重大的漏洞。按照该观点,前缀加和后缀加必须是两个运算符,其中前缀加的优先级高于赋值运算,而后缀加的优先级则低于赋值运算。而所有的C语言教材都承认,自加运算符的优先级只有一个,位于所有运算符中的第二级。这一规定是由ISO/IEC组织在《ISO/ICE 9899:1999,C programming language standard》(以下简称C99标准)中规定,教材作者无权更改。

为解决这一矛盾,明确提出:“把前缀自增(自减)和后缀自增(自减)运算符看成两种运算符,且规定前缀自增(自减)运算符的优先级大于算术运算符,后缀自增(自减)运算符的优先级低于赋值运算符”,即需要修改C99标准。

暂且不提修改C99标准这一愿望是否能实现,即便修改了C99标准暂时解决这一矛盾,但由于这一观点的内涵是错误的,必将会在其他方面暴露出来,下面看一个程序段:

int a[2]={1,2},t;

int*p=a;

t=*p++;

printf("t=%d,a[0]=%d,&a[0]=%p,&a[1]=%p,p=%p",t,a[0],&a[0],&a[1],p);

按照前述“后缀加运算优先级低于赋值号”的观点C99标准中自加运算符位于所有运算符中第二级的规定,该程序的第4行应该这么来理解:“*”的优先级最高,它和p结合,于是将p所指向的存储单元内容(a[0]的值,等于1)赋值给t;然后执行后缀加运算,由于后缀加的优先级低于“*”,因此运算数p应先执行“*”运算后,得到的后果再执行后缀加运算。于是后缀加运算是将p所指向的存储单元(即a[0])的值加1,而p里面存储的地址值不发生变化,还是a[0]的地址值。依次预计该程序输出的结果应为:

t=1,a[0]=2,&a[0]=a[0]的地址值,&a[1]=a[1]的地址值,p=a[0]的地址值

但实际上,任何一个合格的C语言使用者都能凭经验预测出这段代码执行的实际结果应该为:t=1,a[0]=1,p的指针值从a[0]处移动到了a[1]处。该程序实际运行结果如图1所示。

这一结果与前面理论预测不符。为此,谭浩强的书中作了如下解释:“*p++,由于++和*同优先级,是自右而左的结合方向,因此它等价于*(p++)。作用是先得到p指向的变量的值(即*p),然后再使p+1p”。这一解释也受到其他教材作者的赞同。

这一解释单独看起来似乎没有问题,但联系前面的例子“j=i++”,就会发现一个无法解释的矛盾:在“t=*p++”中,后缀加的优先级必须与“*”相同,然后根据结合方向(自右向左)它才有资格与变量p结合,这时它的优先级远高于赋值号“=”。而在“j=i++”中,后缀加的优先级则必须低于“=”,否则结果不对。

从这两个例子可看出,如果按照通常的解释,同是后缀加运算符,在不同的表达式中拥有了两个截然不同的优先级!

更为奇怪的是,即便在同一个表达式“t=*p++”中,为了让++有资格和p结合,规定后缀加的优先级与“*”相同,高于赋值号。但在实际赋值过程中,却是先将p现在所指向的存储单元中的值赋给了t,然后再将p中的地址值加1,这时同一个后缀加的优先级又比赋值号更低。这种忽高忽低的优先级规定,很令人费解。

之所以出现这么奇怪的情况,完全是因为目前教学界的主流观点对于后缀加的错误认识所导致的。

3 正确的解释

其实,关于自加运算符,C99标准中有明确的规定:“++”运算符只有一个,优先级位于第二级。其中前缀加和后缀加的区别在于:按照优先级规定,前缀加直接作用在自加变量上,然后用自加变量本身的值参与后续运算;而后缀加则需要先将自加变量赋值给一个中间变量,然后自加变量加1,再用中间变量参与后续运算。后缀加相对于前缀加而言,其中的差别在于多了一个中间变量,参与后续运算的是这个中间变量,而非自加变量本身。

遵照这一个规定,很容易解释“j=i++”的结果,它实际上执行了以下3步:

ii' //i'为系统自动产生的中间变量

i+1i

i'j //将i自加前的值赋给了j

对于更为复杂的语句“t=*p++”,也可以用同样的方式解释,它实际上执行了以下3步:

pp' //p'为系统自动产生的中间变量

p+1p

*p't

即无论在何种情况下,后缀加始终保持了同一运算优先级,逻辑间没有任何冲突。由此可见,C99标准的规定不仅更权威,也比教学界的主流观点更合理、更简洁。

更进一步,根据C99这一规定,我们还可以对C语言中看上去更难理解的一个问题作出合理的解释。几乎所有的教材都认可这一结论:前缀加比后缀加的效率更高,比如“++k”比“k++”的效率高,但很少有教材指出具体是什么原因。现在知道:这是因为“k++”比“++k”多了一次为中间值赋值的操作。

4 结语

教学界关于后缀加的错误认识流传近20年,影响数百万名学生,却至今还未得到明确的纠正,这一事实令人感到遗憾。

实际上,这一知识理解上的偏差,程序设计者通过大量编程锻炼与思索是可以发现的,而教材作者通过阅读C99标准也能发现。这一错误广泛流传的事实说明,C语言教师和教材作者仅凭阅读几本相关教材以及少量的编程经验是难以真正掌握c语言精髓的。C语言尽管灵活,但仍然是精确的计算机程序设计语言,对任何基础知识理解的偏差,都有可能导致程序设计错误,因此教师有责任为初学者传授准确的知识。这就要求教师和教材作者必须通过实际编程训练和阅读更权威的标准说明,加深和验证自己对C语言的理解。