令我困惑的是,CPPReference说后增量的价值判断是在副作用之前排序的,而预增量却没有这样的保证.

我现在举了一个例子,说明这一点很重要,但我不确定我的分析是否正确.

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

#include <stddef.h>
#include <stdio.h>

int main(void) {
    int arr[] = {0, 1, 2};
    int i = 1;
    int x = ++arr[arr[i]];
}
#include <stddef.h>
#include <stdio.h>

int main(void) {
    int arr[] = {0, 1, 2};
    int i = 1;
    int x = arr[arr[i]]++;
}

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

  1. There are these sequenced-before relations:
    • 值计算i在值计算arr[i]之前进行排序
    • 值计算arr[i]在值计算arr[arr[i]]之前进行排序
    • 值计算arr[arr[i]]在值计算++arr[arr[i]]之前进行排序
  2. 关于这些,++arr[arr[i]]的副作用是没有顺序的.
  3. 编译器可以 Select 满足这些关系的任何顺序,并且可以删除arr[arr[i]]的值计算,因为它不被使用.
  4. 在任何可能的顺序中,arr[arr[i]]的值计算引用与arr[i]相同的标量对象.
  5. ++arr[arr[i]]的副作用修改了标量对象arr[arr[i]],但它的访问顺序与arr[i]的访问顺序不同.

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

然而,我不确定这是否准确.特别是,我不确定如何准确定义递增后/递增前的判断.它是否执行其操作数的值计算?如果是,这是否意味着++*ptr是UB,而(*ptr)++不是UB?如果没有,如何执行完整表达式的值计算-任何运算符都可以访问左值表达式的值,而不对该表达式执行值计算吗?

推荐答案

这个分析是不正确的.具体地说,++arr[arr[i]]的副作用与其他值计算不同.这是因为C标准(Note:我只读过C标准的草案,N3096)规定++E相当于E += 1:

[6.5.16]
The expression ++E is equivalent to (E+=1), where the value 1 is of the appropriate type.

此外,它还指定赋值运算符包括.增强赋值运算符在其副作用之前对其组成表达式的值计算进行排序:

[6.5.3.1]
The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.

C++相关问答推荐

如何确保内存分配在地址附近?

为什么listen()(在调用accept()之前)足以让应用程序完成3次握手?

想了解 struct 指针和空指针转换

ISO_C_BINDING,从Fortran调用C

使用单个字节内的位字段

通过管道将一个子系统的标准输出发送到另一个子系统的标准输出

堆栈帧和值指针

C lang:当我try 将3个或更多元素写入数组时,出现总线错误

二进制计算器与gmp

如何在不使用其他数组或字符串的情况下交换字符串中的两个单词?

调用mProtection将堆栈上的内存设置为只读,直接导致程序SIGSEGV

将数据移动到寄存器时出现分段故障

在循环中复制与删除相同条件代码的性能

如何读取文件并将内容保存在字符串中?(在C语言中,没有崩溃或核心转储错误)

链接到底是如何工作的,我在这里到底做错了什么

判断系统命令返回值的正确方法

是否有单独的缓冲区用于读写库调用?

变量值不正确的问题

WSASocket在哪里定义?

当 n 是我们从用户那里获得的整数时,创建 n 个 struct 参数