资源预览内容
第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
第9页 / 共9页
亲,该文档总共9页全部预览完了,如果喜欢就下载吧!
资源描述
(完整word版)C语言运算符的结合性详细分析C语言运算符的结合性分析吴琼( 鄂州大学计算机系, 湖北鄂州)C 语言与其他高级语言相比, 一个显著的特点就是其运算符特别丰富, 共有34 种运算符.C 语言将这34 种运算符规定了不同的优先级别和结合性。优先级是用来标识运算符在表达式中的运算顺序的, 在求解表达式的值的时候, 总是先按运算符的优先次序由高到低进行操作, 可是, 当一个运算对象两侧的运算符优先级别相同时, 则按运算符的结合性来确定表达式的运算顺序.运算符的结合性指同一优先级的运算符在表达式中操作的组织方向, 即: 当一个运算对象两侧运算符的优先级别相同时, 运算对象与运算符的结合顺序, C 语言规定了各种运算符的结合方向( 结合性) .大多数运算符结合方向是“自左至右, 即: 先左后右, 例如a b+c, b 两侧有- 和+两种运算符的优先级相同, 按先左后右结合方向, b 先与减号结合, 执行a- b 的运算, 再执行加c 的运算。除了自左至右的结合性外, C 语言有三类运算符参与运算的结合方向是从右至左。即: 单目运算符, 条件运算符, 以及赋值运算符。关于结合性的概念在其他高级语言中是没有的, 这是C语言的特点之一,特别是从右至左结合性容易出错, 下面通过几个具体的运算符来剖析C 语言运算符的结合性。若a 是一个变量, 则+a 或a+和 a 或a- - 分别称为前置加或后置加运算和前置减或后置减运算, 且+a 或a+等价于a=a+1, a 或a- 等价于a=a- 1, 即都是使该变量的值增加1 或减少1。由此可知, 对一个变量实行前置或后置运算, 其运算结构是相同的, 但当它们与其他运算结合在一个表达式中时, 其运算值就不同了。前置运算是变量的值先加1 或减1, 然后将改变后的变量值参与其他运算, 如x=5; y=8; c=+x*y; 运算后, c 的值是48,x 的值是6,y 的值是8。而后置运算是变量的值先参与有关运算, 然后将变量本身的值加1 减1, 即参加运算的是该变量变化前的值。如x=5; y=8; c=x+y;运算后, c 的值是40,x 的值是6, y 的值是8.值得注意的是, 前置、后置运算只能用于变量, 不能用于常量和表达式, 且结合方向是从右至左。如当i=6 时, 求 i+的值和i 的值。由于“- ”(负号) “+”为同一个优先级, 故应理解为 (i+), 又因是后置加, 所以先有- i+的值为- 6, 然后i 增值1 为7, 即i=7。例1 main()int a=3,b=5,c;c=a*b+b;printf ( “c=d, c);要得出c 的值, 首先要搞清+的含义。+运算符的结合方向是自右向左的, 如果将表达式理解为:c=a*b+(+b);实际上C 编译器将表达式处理为:c=(a*b+)+b, 因为C 编译器总是从左至右尽可能多地将若干个字符组成一个运算符, 如i+j 等价于(i+)+j。接下来是解决a*b+的问题, 因为+运算符的运算对象只能是整型变量而不能是表达式或常数, 所以a*b+显然是a*(b+)而非(ab)+, 因此整个表达式就是c=(a*(b+))+b.例2 main() int i=1,j;j=i+i+i+;printf( “i=d,j=dn”, i,j);例3 main() int i=1,m;m=+i+i+i;printf( “i=%d,m=%dn”, i,m);j 和m 的值均由表达式求得, 并且这两个表达式均由自增运算符、加法运算符和赋值运算符组成。那么, 它们的值到底为多少呢? j=1+1+1=3 还是j=1+2+3=6? m=2+3+4=9 还是m=4+4+4=12?上机运行结果为: i=4,j=3,m=12。分析: 运算符“+”,“+”和“=”的优先级是递减的, 在计算时,先进行自增运算, 再进行加法运算, 最后是赋值运算。而自增运算又根据“i+”和“+i的不同定义得到不同的值。i+i+i+先将i 原值(1)取出, 作为表达式中i 的值进行加法运算得到3, 然后再实现三次自加; +i+i+i 自加是在整个表达式求解之前开始,也即先对表达式扫描, 对i 进行三次自加得到4, 再进行加法运算得到12.例4 main() int i=1;printf( “i=d, j=%dn”,i,i+i+i+);例5 main() int i=1printf( “i=d,m=dn”,i,+i+i+i);与前两个程序相似, 只不过将表达式移到了函数中作为实参实现调用, 上机运行得: i=4,j=6,m=9.j,m 的值发生了变化。这是因为在实现函数调用时, Turbo C 系统规定, 如果实参中存在表达式, 则按右结合性来计算实参表达式。即运算对象先与右边的运算符结合.i+i+i+即为1+2+3=6; +i+i+i, 即为2+3+4=9.所以当含“+”运算符的表达式作为实参实现调用时, 遵守右结合性原则。例6 设a=6, 求赋值表达式a+=a- =a a*a 的值.由于“”(乘号)、“- ”(减号)优先级高于“+=”、“- =”, 且“优先级高于“ ”, 故先求a- aa, 即6 66= 30, 由“+=”,“- =为同一优先级, 且是从右至左的结合方向, 再求a- = 30, 即a=a ( 30)=6+30=36, 最后求a+=36, 即a=a+36=36+36=72, 所以赋值表达式的值为a=72。例7 设m=1,n=2,b=3, 求赋值表达式m+=n = - - b 的值。这里共有四个运算符“+=、“- =”、“ (负号)、“- - ”, 由运算符优先级, 应先计算- b, 但“- - ”与“- ”(负号)优先级相同, 如按从右到左的结合方向, 它可能是- ( b), 也可能是 - (- b), 究竟是哪一个呢?前面已讲过, 前置运算只能用于变量, 不能用于表达式, 而(- b)不是一个变量, 而是表达式, 故只能是- ( b), 即为- (3- 1)=- 2; 然后计算n- =- 2, 即n=n (- 2)=2- ( 2)=4; 最后计算m+=4, 即m=m+4=1+4=5, 所以赋值表达式的值m=5.C 语言中运算符的两种不同的结合方向是其特有的, 理解结合方向有助于理解表达式的运算顺序。当看到一个复杂的C 语言表达式时, 首先应按优先级进行运算, 然后在同一优先级中按结合方向进行运算。而后者是初学者最易出错的, 本文通过若干典型实例分析, 希望对读者理解C 语言运算符结合性有所帮助.参考文献:1谭浩强。C 程序设计( 第二版) M.北京:清华大学出版社,1999.2刘祎玮,汪晓平。C 语言高级实例解析M.北京:清华大学出版社,2004.3Herbert Schildt。著,戴健鹏.译.C 语言大全( 第二版) M.北京:电子工业出版社,1994。C语言中怎样理解三目运算符(条件运算符)的右结合性?对于表达式 -a = b+ ? a+ : b+当然是先判断-a 是否等于 b+ ,然后决定执行后面的哪个表达式。问题:但是它的又结合性体系那在哪里呢?为什么不把这个表达式中所有的自加自减运算先执行完,然后再执行条件判断呢?解析:这里涉及了C中的优先级、结合性、求值顺序。结合性只有在相同优先级的运算符间才起作用,比如a+bc,+与优先级不同,这里根本不用去管结合性。而a+b+c则需要进行结合性考虑了,如果+为左结合性,那么应该理解为:(a+b)+c,如果+为右结合性,那么应该理解为a+(b+c),当然了,我们已经知道+为左结合性了.对于?:,在C中与它优先级相同的只有它自己,因此只有连续的?:才会体现出它的右结合性,即a?b:c?d:e中,根据右结合性可知应理解为a?b:(c?d:e)。显然,上述问题中是体现不出这种结合性来的。再者,还有一个求值顺序的问题,a?b:c中,C语言规定先对a求值,非零则对b求值并作为该表达式的值,为零则对c求值并作为表达式的值,并且b和c中有且仅有一个会被求值.对于题目中的就应该是:先进行(-a = b+)的求值,根据是否为零,会对(a+)或(b+)进行求值。说白了,也就是只有两种可能性:可能性1:先算(-a = b+),再算(a+);可能性2:先算(-a = b+),再算(b+)。至于(-a = b+)中到底是先算-a 还是b+则仅从C语言这个角度是无法判别的,这是个实现问题,如果要想写出健壮的可移植的代码就应该避免这种表述.总结一下,就是先看优先级,次看结合性,有的求值有序,有的则无序。实际上,一条语句中出现多次同一个变量的自增或自减是不合规范的,因为这种写法出现的结果是不确定的,根据编译器而定。a+表示语句执行后a = a + 1,到底有多后?有2个a+怎么办?这都是编译器内部机制的问题。真正好的程序员是避免这种情况的.C语言运算符优先级普遍存在的一个深层次误区国内许多C语言教科书,在介绍C语言运算符时,都把所谓的“单目运算符”归纳为优先级相同结合性从右向左的运算符.例如号称是“我国广大初学者学习C语言程序设计的主流用书”的C程序设计(谭浩强著,清华大学出版社,2010年6月(第四版))的378页附录D 运算符和结合性 中就是这样:殊不知,这种归纳是完全错误的。而且恰恰由于C程序设计是所谓的“主流用书”,其错误带来的影响也是广泛普遍的和灾难性的.(google或百度一下“所有的单目运算符具有相同的优先级”,你就会知道我是不是在夸大其词危言耸听)。 为了揭示“所有的单目运算符具有相同的优先级的错误,下面首先按照这种错误的说法进行一个实验。我们都知道,对于int i;来说,&i是求得一个指向i的指针(注意这里的“”是一个“单目运算符),i的数据类型显然是“int .如果对“int *”类型的表达式“i”做“(int )”类型转换运算(可能显得有点无聊)(int )i得到显然还是“i”值和类型都没有任何改变。 按照“所有的单目运算符具有相同的优先级”这个错误的说法,由于“&” 和“(int )”的结合性从右向左 (int *)&i 这个表达式没有任何毛病,也不需要通过加“()来明确运算对象。现在,再对 (int )i 这个表达式做sizeof运算,由于sizeof也和(int )同级(注意这是错误的),结合性从右向左,所以可以直接把sizeof写在(int )&i 的左面,即 sizeof (int ) & i显然,这个表达式的运算结果和sizeof (int *)应该一模一样,因为(int ) i的数据类型是(int ) 。 然而,如果你在机器上跑一下下面的代码的话include stdio。h include stdlib。h int main( void ) int i ; printf(” u , sizeof (int *) ) ; printf(” %u ” , sizeof (int ) & i ) ; system(PAUSE); 这句话的究竟有什么错误呢
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号