我在这里正确解释了 C 的操作顺序吗?

sch*_*ine 5 c sequence-points language-lawyer order-of-execution

我对CPPReference说 postincrement\xe2\x80\x99s 值评估在其副作用之前排序这一事实感到困惑,但 preincrement 没有这样的保证。

\n

我现在想出了一个例子,这很重要,但我不确定我的分析是否正确。

\n

据我了解,这两个程序的不同之处在于第一个包含 UB,而第二个则不包含:

\n
#include <stddef.h>\n#include <stdio.h>\n\nint main(void) {\n    int arr[] = {0, 1, 2};\n    int i = 1;\n    int x = ++arr[arr[i]];\n}\n
Run Code Online (Sandbox Code Playgroud)\n
#include <stddef.h>\n#include <stdio.h>\n\nint main(void) {\n    int arr[] = {0, 1, 2};\n    int i = 1;\n    int x = arr[arr[i]]++;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我对这个表达式的分析++arr[arr[i]]如下:

\n
    \n
  1. 有以下先序关系:\n
      \n
    • 的值计算在 的i值计算之前排序arr[i]
    • \n
    • 的值计算在 的arr[i]值计算之前排序arr[arr[i]]
    • \n
    • 的值计算在 的arr[arr[i]]值计算之前排序++arr[arr[i]]
    • \n
    \n
  2. \n
  3. ++arr[arr[i]]与这些相关的副作用是无序的。
  4. \n
  5. 编译器可以选择满足这些关系的任何顺序,并且可以删除 的值计算,arr[arr[i]]因为它没有被使用。
  6. \n
  7. 在任何可能的顺序中, 的值计算arr[arr[i]]引用与 相同的标量对象arr[i]
  8. \n
  9. 的副作用是++arr[arr[i]]修改标量对象arr[arr[i]],但它的访问顺序与 by 的顺序无关arr[i]
  10. \n
\n

然而,如果我们使用后增量,我们会引入一个新的序前关系: 的值计算arr[arr[i]]++在其副作用之前排序。因此,通过传递性,副作用不再是无序的arr[i]

\n

但是,我不确定这是否准确。特别是,我不确定增量/增量前的评估是如何准确定义的。它是否对其操作数执行值计算?如果是的话,这是否意味着++*ptrUB 是 UB 而(*ptr)++不是?如果不是,那么如何执行完整表达式的值计算\xe2\x80\x94任何运算符都可以访问左值表达式的值而不对该表达式执行值计算吗?

\n

sch*_*ine 1

分析不正确。特别是, 的副作用并非++arr[arr[i]]与其他值计算无序。这是因为 C 标准(:我只读过 C 标准的草案,N3096)指定++E相当于E += 1

\n
\n

[6.5.16]
\n表达式 ++E 等价于 (E+=1),其中值 1\n 属于适当的类型。

\n
\n

此外,它还指定了赋值运算符,包括。增强赋值运算符的组成表达式\xe2\x80\x99 值计算在其副作用之前排序:

\n
\n

[6.5.3.1]
\n更新左操作数的存储值的副作用在左操作数和右操作数的值计算之后排序。

\n
\n