自增自减运算符++/–与函数调用

同事在从Keil向IAR移植时遇到一个函数调用产生的问题,细究原因,应该是 ++ 运算符和函数赋值顺序引起的。先看如下代码:

#include   \<stdio.h\>
void test_func(char a, char b)
{
  printf("\r\na=%d b=%d",a,b);
}

void main(void)
{
  char buf[3] = {1,2,3};
  char i = 0;
  char c = 0,d = 0;
  c = i++ + i++;
  printf("\r\nc=%d i=%d",c,i);
  i=0;
  printf("\r\npara1=%d para2=%d",i++,i++);
  i = 0;
  test_func(buf[i++],buf[i++]);
}

上面代码的输出结果如下:

c=0 i=2
para1=1 para2=0
a=2 b=1

可以看出:
1.自加运算符是在整条语句执行完后才操作
2.函数调用时参数赋值时多条语句实现的,且上面在cygwin中函数参数的赋值是从右往左的(C标准并没有固定函数参数的赋值顺序)

结论:不在同一行C代码中使用多个自增自减运算符。

PS:转载一篇++/–运算符的详细总结,原文地址

C语言由于其功能强、使用灵活、可移植性好、目标程序质量高而受到广泛的欢迎。同时由于C语言语法限制不严格,程序设计自由度大,在使用时会出现一些“副作用”。因此掌握C语言要比掌握其它任何一门语言相对要难一些,尤其是对C语言中++和――运算符的使用。正确而又灵活地使用好++/――运算符,可以编写出风格简洁、灵活高效的源代码,极大地提高编程的效率。但由于它们的使用过于灵活多变,不易掌握;再加上许多教科书和杂志上对这个问题讲得不够深入透彻,致使许多初学者对此感到格外头疼。笔者根据多年从事C语言编程的经验,把C语言中++/–运算符的使用做了一个总结,希望对C语言的初学者有一个帮助。笔者认为,++/――运算符的使用可以从以下的几个方面来理解:

一、它们只能用于单个的整型或字符型的变量。

++/――运算符只可以用于单个的变量,而不能用于常量或表达式,这个变量必须是整型或字符型的,C语言中这样的设置是有道理的。试想一下,这个运算符表示的是“在原来的基础上增加或减少一个”,最后的结果还是要存回到原来的变量中去,如果是表达式,自增或自减后它的值保存到哪里去呢?如果是浮点型或双精度型,数据的存储形式是复杂的,加上一位后会引起数值的混乱,因此用于单个的整型变量是合适的。字符型的变量与整型的变量在C语言中是通用的,针对整型数据的操作也同样适用于字符型的变量,把它应用于单个的字符变量时,表示其中的美国信息交换标准代码值增加或减少一个。

但-i++是正确的,因为在C语言中负号运算符和“++”运算符同优先级,而结合方向为“自右至左”(即右结合性),因此上式相当于-(i++),如果i的初值为3,有j=-i++,则运算过程为先取出i的的原值3,把-3赋值给j,然后i的值加1变成4。

二、++/――运算符放在单个变量的前面时称为“前置”,放在单个变量的后面时称为“后置”。

当用于单独的一条语句时,前置和后置所表示的含义是相同的,即都表示把某个变量的值在原来的基础上增减一个。例如:当出现了以下的程序段时,++/――运算符前置或后置表示的含义相同。例1:int x=8;x++; /* x=9 */,例2:int x=8; ――x; /* x=7 */

三、用作赋值语句时前置运算表示的是“先增减后赋值”;后置运算表示的则是“先赋值后增减”。

++和――运算符用在赋值语句中时,前置运算是变量的值先增减后再参与赋值操作,后置运算则是变量的值先参与赋值运算,然后变量的值再进行增减。假设有以下的一个程序段,每条语句的执行完毕后各变量的值写在其后的注释中。

int x=5,y=6,z;

1、 z=x++; /* 执行后z=5,x=6 */    2、 z=++y; /* 执行后z=6,y=6 */

3、  z=y――; /* 执行后z=6,y=5 */   4、 z=――x; /* 执行后z=5,x=5 */

从以上的变化过程中可以看出,第1条语句执行的过程是:先把x的值赋给z,再将x中的值自增1,故而z得到了x赋值前的值5,x自增1后变成了6;第二条语句执行的过程是:先把x的值自减了1,由6变成了5,再将5赋给变量z;第3条语句和第4条语句同1、2条语句执行的道理是一样的。因此,++/――运算符用在赋值语句中时前置和后置所表示的含义是截然不同的。

四、当用于四则运算中时,前置的++/――运算符优先级最大,后置的++/――运算符优先级最小,其它运算符居中。

在解题的过程中经常会遇到++/――运算符与+、-、*、/、%等四则运算符混合运算的情况。在这里必须要把握住这样的一条原则:++/――运算符前置时优先级最大,在参与运算的所有其它运算符运算前运算;后置时的优先级最小,要在所有其它的运算符执行完毕后再来运算它。假设有如下的一个例子:

int i=5,j=i++*i――*――i;

在j的赋值语句后面是一长串的加减号,初学者很是觉得头疼,但只要把++/――运算符的使用特点搞清楚,理解起来并不是很难。前面说过,++/――运算符只可以用于单个的变量不可用于表达式,同时C编译系统在处理时尽可能多地自左至右将若干个字符组成一个运算,故而上述赋值语句的优先关系应以下面的形式来表示:j=(i++)*(i――)*(――i),并且只能有这样一种表示方式。

在确定了各个运算符的优先级关系后,下一步就按照上面所讲的规则来进行运算,前置的运算符优先级最高,故而要先算――i,i的值就变成了4;再算j=i*i*i=64,最后再来依次地算i++和i――,最后i的值是4。在这道例题里,初学者常犯的错误就是乱套第三点所指出的规则,从前往后依次计算,这样就会得出j=4*5*3=60这样错误的结论。

五、++和――运算符用于逻辑表达式中时,它们的优先级关系受到逻辑运算符特点的制约。

我们首先来看这样的一个例句:

int x=y=z=0;

++x||++y&&++z;

printf(“x=%d,y=%d,z=%d\n”,x,y,z);

如果按照上一条的运算规则,应该先计算++x,++y和++z;把三个变量中的值均增加1,再执行x||y&&z;那么输出的结果应该是x=1,y=1,z=1,但实际的结果却是x=1,y=0,z=0。也就是说,在第三行逻辑表达式中,只有++x被运算了,后面的++y和++z都没有被运算。因为C语言中对逻辑表达式有特殊的规定:在一个复杂的表达式中,如果通过前面的表达式的值能够判断出整个表达式的值,就没有必要再计算后面的表达式了。这样的设置是为了加快逻辑表达式的执行速度。在这道例题里,第一个表达式++x的值为真,由于其后就是逻辑或运算,所以不论后面表达式的值是真是假,结果恒为真,++y与++z就不会执行了。这个例子充分地说明,在运用前置后置的规则时,还要注意到其它运算符的使用特点,要综合起来考虑问题。

六、当用于函数的实参时,应注意实参向形参赋值的顺序。

C标准没有具体规定函数参数求值的顺序是自左至右还是自右至左。但每个C编译程序都有自己的顺序,在有些情况下自左至右和自右至左的结果是相同的,如fun(a+b,b+c,c+a),但如实际参数含有++/–运算符时,情况就有些不同了。在Turbo C和MS C系统中规定函数实参向形参赋值时,是按照自右而左的顺序进行赋值的,也就是说最后一个实参先把值赋给最后一个形参,倒数第二个实参再把值赋给倒数第二个形参……,最后才把第一个实参赋给第一个形参。如果在实参中不涉及到++/――运算符,那么从前往后和从后往前理解都是一样的。但如果在实参中使用了++/――运算符,那么这个赋值的过程就显得尤为重要了。让我们来看下面的这个例子。

int add(int x,int y){   return  (x*y);  }

int main(){

int i=5, j=add(i++,++i);

}

这个调用的过程可以这样来表示:

 

 

add(i++,        ++i)

 

add(int x,       int y)

 

首先把实参++i赋给y,即y=++I;i的值先变成6,y的值也被置成6;再执行x=i++,先把i的值赋给x,使它变为6,再将i自增一个变为7。所以返回值应是6*6=36;如果将它理解为从前向后赋值,其结果是5*7=35,就不正确了。因此,在用++/――运算符作为函数参数时,一定要注意它的实参向形参赋值的顺序,避免出错。

经过长期教学工作的检验,对以上的这几条运算法则,只要能够熟练掌握,融汇贯通,对任何一种有关++/――的运算都能顺利解决。

分享到: 更多
版权申明:

本站保留所有原创文章的版权,本站地址:奔跑的博客[http://www.elecbench.com]

原创文章转载时请注明出处,并添加文章所在页面的链接:http://www.elecbench.com/%e8%87%aa%e5%a2%9e%e8%87%aa%e5%87%8f%e8%bf%90%e7%ae%97%e7%ac%a6-%e4%b8%8e%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8/

本站所有 2010年3月4日 以后发表、未标明为“转载”的文章均是本站原创。

发表评论


(设置自己的个性头像)

*

申请属于你的免费顶级域名