Q: 3.11 我如何避免这些未定义求值顺序困难,如果我看起来没有明白那些复杂的规则?


A: 最简单的回答是,如果你避开没有相当明显解释的表达式,你将也在最大程度避免未定义行为。(当然,“相当明显”对不同的人表示的意思不同。这个回答会有效,如果你同意a[i] = i++和i = i++不是“相当明显”。)

更精确一点,这里有一些更简单的规则,它们尽管比标准更保守一些,有助于确保你的代码“相当明显”和对编译器与你同伴的程序员同样可理解:

  1. 确保每个表达式至多修改一个对象。我们提到“对象”,意思是一个简单变量,或一个数组元素,或指针指向的位置(例如*p)。一个“修改”是使用=运算符的简单赋值,或使用像+=、-=、*=,或使用++、--(前缀或后缀)的自增、自减复合赋值。

  2. 如果一个对象(同以上定义)多次出现在一个表达式中,并且是在表达式中被修改的对象,确保所有出现并获取该对象值的地方,涉及的是已存储的新值的计算结果。这个规则允许这个表达式

      i = i + 1
    

    因为尽管对象i出现两次并被修改,它获取i的旧值的出现(右边)被用于计算i的新值。

  3. 如果你想打破规则1,确保多个对象的修改是完全不同的,并尝试限制自己修改两个或至多三个,使用匹配下列例子的风格。(也要确保对每个被修改的对象,你都继续遵守规则2。) 表达式

      c = *p++
    

    在该规则下被允许,因为两个被修改的对象(c和p)是不同的。表达式

      *p++ = c
    

    也被允许,因为p和*p(即p本身和它所指向的对象)都被修改但是几乎肯定是不同的。同样地

      c = a[i++]
    

      a[i++] = c
    

    两者都被允许,因为c、i和a[i]大概都是不同的。最后这样的表达式

      *p++ = *q++
    

      a[i++] = b[j++]
    

    它们中三个对象被修改(在第一个表达式中p、q和*p,第二个表达式中i、j和a[i])被允许,如果三个对象是不同的。即仅当使用了两个不同的指针p和q或两个不同的数组索引i和j。

  4. 你也可以打破规则1或2,当你在两次修改之间,使用了一个已定义顺序点的运算符,或在修改和访问之间。表达式

      (c = getchar()) != EOF && c != '\n'
    

    (常见于一个读取整行输入的while循环中)是合法的,因为变量c的第二次访问出现在&&隐含的顺序点之后。(没有顺序点,这个表达式将是非法的,因为在右边与'\n'相比较时对c的访问,不能在左边“确定用于存储的值”。)


(This Chinese translation isn't confirmed by the author, and it isn't for profits.)

Translator : jhlicc@gmai1.c0m
Origin : http://www.c-faq.com/expr/confused.html