i++与++i的收集

1.先给出结论:

i++ = 5;    错

++i = 5;    对

原因为何:i++的运算结果并不是i这个变量的引用,而是一个临时变量,其值为i的值,所以你无法进行以下运算:   
  i++=5;   甚至编译器不允许你对一个临时变量重新赋值,上面的表达式会引起编译错误   。

左值,说得通俗一点,就是可以出现在表达式左边的值(等号左边),可以被改变。   
  a   =   5;   
  a是左值。   
  不能被改变的值不是左值,常量像5,'a',"abc123"这种都不能做左值。   
  自加/减运算符的操作数如果不是左值就不能自加和减。   
  但i++的操作就不能作左值,它返回一个临时变量。++i可以是左值,它返回i被加1后的自己。a   =   (i++)   *   5   ;   
  ==>   temp   =   i;   
          i   =   i   +   1;   
          a   =   temp   +   5;   
  a   =   (++i)   *   5;   
  ==>   i   =   i   +   1;   
          a   =   i   *   5;   
  temp没有在你的程序中出现,那是编译器自动做的,你不能给temp赋值。

笔试碰到一题目:

1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)
int a = 4;
(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);
a = ?
答:C 错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;
改后答案依次为9,10,10,11

 

 

2. 不要再问i++++i啦!

好多人面试的喜欢问i++++i的区别。尤其是对应届毕业生面试中尤为常见。更有主考官原意出“x = 4; x = (4 + x++) + ( ++x )”这种难题,觉得这种题可以综合考察被面试者的综合能力。不排除有人,能把这种题答的很完美,但也不排除有的主考官连自己都搞不明白。

先不说这题出的对错,这种问题,在大部分(觉大部分)开发中又有多少机会会被使用到??

接着我说这种的出的是否合理。

首先介绍两个我在下面会提到的术语:

副作用(side effect):指的是在计算表达式时对某些东西(如存储变量中的值)进行修改。

顺序点(sequence point):是指程序执行过程中的一个点,在这里,进入一步之前将确保对所有的副作用都进行了评估。在C++中,语句的分号就是一个顺序点,这意味着程序处理下一条语句之前,赋值操作符、递归操作符和递减操作符执行的所有修改必须完成。

何为一个完整的表达式呢?例子有:表达式语句中的表达式部分以及用作while循环中检测条件的表达式。

顺序点有助阐明后缀递增何时进行。

现在来看下面语句:y = (4 + x++) + (6 + x++);

表达式4 + x++ 不是一个完整的表达式,因此,C++不能保证x的值计算子表达式4 + x++后立刻增加1。在这个例子中整条赋值语句是一个完整的表达式,而分号表示里顺序点,因此C++ 只能保证程序执行大下一条语句之前,x的值增加两次。C++没有规定是在计算每个表达式之后将x的值递增,还是在表达式计算完毕后才将x的值递增,有鉴于此,您应避免使用这样的表达式。

上面这种情况指的是ISO/ANSI C++标准第二版(ISO/IEC 14882:2003)。但是不同的C++编译器厂商在此基础上的标准也不同,应该说都是对这种标准的继承。

所以说这是一道毫无意义的题。

 

3.副作用与顺序点


表达式的计算分为两种,一种是有副作用的计算,如:
(++x)+y
一种是无副作用的计算,如:
x*y

有副作用的计算中,子表达式的计算顺序是重要的。例如
(++x)*(x+1)
当x=0时,如果先算++x,上式计算结果为2,如果先算x+1,上式计算结果为1。
再如,对函数g(int, int)的调用g(x, ++x), 当x=1,这个调用是g(1, 2)还是g(2, 2)?

所谓“顺序点”,和表达式的副作用紧密相关。再看这个例子:

(++i) + (++j)

这个表达式的计算,有两个副作用:
i自增1;
j自增1;
但是到底哪一个先发生?答案是:任何答案都不对。

为什么?因为标准并不定义副作用发生的顺序。标准只保证,一个表达式的全部副作用,不在达到该表达式紧邻的前一顺序点前发生,并且一定在达到该表达式紧邻的下一个顺序点之前发生完毕。

一个顺序点,被定义为程序执行过程中的这样一个点:该点前的表达式的所有副作用,在程序执行到达该点之前发生完毕;该点后的表达式的所有副作用,在程序执行到该点时尚未发生。

(++i) + (++j)这个表达式本身不包含顺序点,所以i++,j++这两个“副作用”到底谁先发生,根据标准,是未定义的。如果给这个表达式加上顺序点,如:
;(++i) + (++j);
标准只保证,这两个副作用在整个表达式求值完成前(即到达后面的顺序点”;”前)都会发生,并且不会在上一个语句执行完毕之前发生。

标准还规定,两个相邻顺序点之间,对某一表达式求值,最多只能造成任一特定对象的值被更改一次。如果表达式求值过程会更改某对象的值,那么要求更改前的值被读取的唯一目的,只能是用来确定要存入的新值。
例如下面的表达式,按照标准规定,执行结果是未定义的:
(i++)+(i++)
这个表达式本身不包含任何顺序点,但是对这个表达式求值,按照运算符定义,将更改i两次,违反了“一次更改”的要求。
再看下面的表达式,按照标准规定,执行结果也是未定义的:
x=i++
这个表达式本身不包含任何顺序点,虽然i的值只更改了一次,但是x这个左值中,i被读取,用于确定数组中被修改的元素的下标。这次对i求值和i++肯定位于同一对顺序点之间,该表达式求值过程更改了i的值,x中读取i却不是为了确定i的新值,这违反了“读取只能用于确定新值”规定。

任何对相邻顺序点间表达式求值的多个副作用发生的顺序进行假设,或者违反上述“一次更改、读取仅用于确定新值”规定的代码,其执行结果都是未定义的。这里所说的“未定义”,通常比“不可移植”更严重,可以认为是“错误”的同意词。

通常我们认为,标准对“顺序点”及其语义的定义,是为了严谨地定义C/C++的表达式和求值过程,并不是为了让程序员通过对顺序点的掌握,(过分 地)利用表达式求值的副作用。实际工作中,我们完全可以通过引入中间变量,避开“顺序点”这样容易出错,也极大地降低代码可读性的“边缘概念”。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值