c++ 中 `++i` 与 `i++` 在运算表达式中的优先级

来源:互联网 发布:淘宝客服是怎么做的 编辑:程序博客网 时间:2024/06/06 13:56

在算术表达式中,优先级高的运算符先运算,优先级低的运算符后运算,不同的优先级直接影响表达式的计算结果。

1. 说明

  • gcc中
    gcc中的加法运算表达式中,是按照从左到右按顺序,如果运算符两边有++i操作数,就先进行++i操作,然后进行加法运算;

  • vs中
    vs中的加法运算表达式中,则不一样,只要表达式中有++i操作数,就要先计算,最后才是进行加法运算。

加法运算可以扩展到减法、乘法、除法运算和前置–、后置–。但是如果是四则混合运算还要考虑加、减、乘、除的优先级问题。

2. 实例1

计算如下表达式的值:

int i = 3, j = 0;j = (i++) + (i++) + (++i);cout << "i = " << i << endl;   cout << "j = " << j << endl;

结果:

  • gcc
    i = 6
    j = 10

  • vs2012
    i = 6
    j = 12

计算过程:

  • gcc

    1. 从左到右:fun = (i++) + (i++) = 3 + 3 = 6
    2. j = fun + (++i),表达式中有(++i)
      1. 先计算(++i): i = 4
      2. j = fun + i = fun + 4 = 10
    3. 计算左边(i++): i = 5
    4. 计算中间(i++): i = 6
  • vs2012

    1. 表达式中有(++i),先计算 :i = 4
    2. 从左到右:fun = (i++) + (i++) = i + i = 4 + 4 = 8
    3. j = fun + i = 8 + 4 = 12
    4. 计算左边(i++): i = 5
    5. 计算中间(i++): i = 6

3. 实例2

计算如下表达式的值:

int i = 3, j = 0;j = (++i) + (++i) + (++i);cout << "i = " << i << endl;cout << "j = " << j << endl;

结果:

  • gcc
    i = 6
    j = 16

  • vs2012
    i = 6
    j = 18

计算过程:

  • gcc

    1. 从左到右:fun = (++i) + (++i)
      1. 先计算左侧(++i):i = 4
      2. 再计算右侧(++i):i = 5
      3. 然后计算 :fun = i + i = 5 + 5 = 10
    2. j = fun + (++i),
      1. 先计算右边(++i): i = 6
      2. 然后计算 :j = fun + i = 10 + 6 = 16
  • vs2012

    1. 表达式中有(++i),先计算
      1. 先计算左侧(++i):i = 4
      2. 再计算中间(++i):i = 5
      3. 先计算右边(++i): i = 6
    2. 从左到右:fun = i + i = 6 + 6 = 12
    3. j = fun + i = 12 + 6 = 18

4. 实例3

计算如下表达式的值:

int i = 3, j = 0;j = (++i) + (++i) + (++i) + (++i);cout << "i = " << i << endl;cout << "j = " << j << endl;

结果:

  • gcc
    i = 7
    j = 23

  • vs2012
    i = 7
    j = 28

计算过程与实例 1 和实例 2 类似,就不再推导。

5. 理解

为什么同是 c++,同样的代码在 gcc 下和 vs 下的结果会不一样呢?下面,先从几个概念说起:

  • 表达式的副作用

    • 含义:表达式在求值过程中要改变该表达式中作为操作数的某个变量的值。
    • 原因:表达式中包含了具有副作用的操作符,这样的操作符包括:赋值操作符、复合赋值操作符、增1减1操作符。如:j = (i++) + i + (i++)
  • 顺序点

    • 含义:也称作序列点,是计算机程序中一些执行点,在该点处之前的求值的所有的副作用已经发生,在它之后的求值的所有副作用仍未开始
    • 例子:for(int i = 0; i < N; y = i++)中的两个分号就是顺序点,把int i = 0i < Ny = i++分开。

c++ 标准只保证,在一个顺序点处,求值和副作用全部完成,然后才能进入下面的部份,但是在两个顺序点直接,副作用的顺序并没有规定。所以在多个副作用同时存在的情况下,不同的编译器会有不同的执行顺序。

实际工作中,完全可以通过引入中间变量,避开“顺序点”这样容易出错,也极大地降低代码可读性的“边缘概念”。

6. c 与 c++ 顺序点出现的位置

  1. && (逻辑与)、 || (逻辑或)、逗号操作符的左操作数与右操作数求值之间(前两者是短路求值的一部分)。例如,表达式*p++ != 0 && *q++ != 0,子表达式*p++ != 0的副作用都会在试图访问q之前完成。
  2. 三元条件运算符的第一个操作数之后,第二或第三操作数之前。例如,表达式a = (*p++) ? (*p++) : 0在第一个*p++之后存在顺序点,因而在第二个*p++求值之前已经做完一次自增。
  3. 完整表达式结束处。包括表达式语句(如赋值a=b;),返回语句,if、switch、while、do-while语句的控制表达式,for语句的3个表达式。
  4. 函数调用时的函数入口点。函数实参的求值顺序未指定,但数序点意味着这些实参求值的副作用在进入函数时都已经完成。表达式f(i++) + g(j++) + h(k++),调用f(), g(), h()的顺序未指定,i, j, k的自增顺序也未指定。函数调用f(a,b,c)的实参列表不是逗号运算符,a, b, and c的求值顺序未指定。
  5. 函数返回时,在返回值已经复制到调用上下文。(仅C++标准指出这一顺序点)
  6. 初始化的结束。例如,声明int a = 5;中的对5求值之后。
  7. 在声明序列的每个声明(declarator)之间。例如,int x = a++, y = a++的两次a++求值之间。注意,此例不是逗号运算符。

7. 参考

参见维基百科

0 0
原创粉丝点击