• i++ : 顺序点(sequence point) 与 副作用 (side effect)


    表达式的计算分为两种,一种是有副作用的计算,如:
    (++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++的表达式和求值过程,并不是为了让程序员通过对顺序点的掌握,(过分 地)利用表达式求值的副作用。实际工作中,我们完全可以通过引入中间变量,避开“顺序点”这样容易出错,也极大地降低代码可读性的“边缘概念”。



    关于 “未定义” (undefined),参考:http://en.wikipedia.org/wiki/Undefined_behavior

    关于 “顺序点”(sequence point),参考:http://en.wikipedia.org/wiki/Sequence_point
  • 相关阅读:
    SpringBoot集成Mybatis
    springboot通过slf4j配置日志
    SpringBoot导入jsp依赖始终报错
    shiro小记
    阿里开发手册华山版——(编程规约篇)记录目前自己不合理的地方
    [网络流24题] 2. 太空飞行计划问题 解题报告
    LOJ 6089 小 Y 的背包计数问题 解题报告 (动态规划)
    UVA 10599 Blocks 解题报告 (动态规划)
    Comet OJ#12E Ternary String Counting 解题报告
    [WC2016]挑战NPC 解题报告
  • 原文地址:https://www.cnblogs.com/smwikipedia/p/1229984.html
Copyright © 2020-2023  润新知