jac*_*k X 17 c++ language-lawyer c++17
#include <iostream>
int main(){
int arr[7] = {0,1,2,3,4,3,2};
arr[0]++[arr]++[arr]++[arr]++[arr]++[arr]++[arr] = 5; //#1
for(auto i = 0;i<7;i++){
std::cout<<i<<" : "<< arr[i]<<std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
考虑上面的代码,这个评估#1是否会导致 UB?这是我在twitter 上看到的一个例子。
根据 postfix ++ 的求值顺序:
expr.post.incr#1
++ 表达式的值计算在修改操作数对象之前进行排序。
这意味着,这样的例子会导致 UB
int arr[2] = {0};
(*(arr[0]++ + arr))++
Run Code Online (Sandbox Code Playgroud)
因为,表达式引起的副作用arr[0]++和(*(arr[0]++) + arr))++未排序并应用于相同的内存位置。
但是,对于第一个示例,这是不同的。我的论点是:
expr.sub#1
表达式 E1[E2] 与(根据定义)相同,*((E1)+(E2)),...,表达式 E1 在表达式 E2 之前排序。
这意味着,与 E1 相关的每个值计算和副作用都在与 E2 相关的每个值计算和副作用之前排序。
为了简化表达式 at #1,根据表达式的语法,这样的表达式应该符合:
expr.ass
逻辑或表达式赋值运算符初始化子句
当logical-or-expression这里是一个后缀表达式。那是,
postfix-expression [ arr ] = 5;
Run Code Online (Sandbox Code Playgroud)
后缀表达式的形式为postfix-expression ++,而postfix-expression的形式为postfix-expression[arr]。简单来说,赋值的左操作数由两种后缀表达式组成,它们相互交替组合。
后缀表达式从左到右分组
所以,让下标操作有形式E1[E2],后缀++表达式有形式PE++,那么对于第一个例子,它会给出如下分解:
E1': arr[0]++
E2': arr
E1'[E2']: arr[0]++[arr]
PE'++ : E1'[E2']++
E1'': PE'++
E2'': arr
E1''[E2'']: PE'++[arr]
PE''++: E1''[E2''] ++
and so on...
Run Code Online (Sandbox Code Playgroud)
这意味着,为了计算PE'++,E1'[E2']应该先计算PE'++,这与 *((E1')+E2') 相同,每个规则E1'都在 之前排序E2',因此 引起的副作用E1'在 的值计算之前被排序E2'。
换句话说,由 postfix++ 表达式引起的每个副作用必须在该表达式与随后的[arr].
因此,通过这种方式,我认为这样的代码#1应该具有明确定义的行为而不是 UB。我有什么误解吗?代码是不是UB?如果它不是 UB,代码将给出的正确结果是什么?
我相信您的理解很好,并且代码在 C++17 中的 C++ 中也很好。
我有什么误解吗?
不。
代码是不是UB?
不。
如果不是UB,结果是什么?
arr[0]++[arr]++[arr]++[arr]++[arr]++[arr]++[arr] = 5;
Side effect: arr[0] := 0 + 1 = 1
0[arr]++[arr]++[arr]++[arr]++[arr]++[arr] = 5;
Side effect: arr[0] := 1 + 1 = 2
1[arr]++[arr]++[arr]++[arr]++[arr] = 5;
Side effect: arr[1] := 1 + 1 = 2
1[arr]++[arr]++[arr]++[arr] = 5;
Side effect: arr[1] := 2 + 1 = 3
2[arr]++[arr]++[arr] = 5;
Side effect: arr[2] := 2 + 1 = 3
2[arr]++[arr] = 5;
Side effect: arr[2] := 3 + 1 = 4
3[arr] = 5;
Side effect: arr[3] := 5
Run Code Online (Sandbox Code Playgroud)
我看到输出将是:
0 : 2
1 : 3
2 : 4
3 : 5
4 : 4
5 : 3
6 : 2
Run Code Online (Sandbox Code Playgroud)
请注意,该部分The expression E1 is sequenced before the expression E2是在 C++17 中添加的。
代码在 C++17 之前是未定义的,在 C 中是未定义的(推文是关于 C 代码的),因为在from上的arr[0]++[arr]++两个副作用是彼此未排序的。arr[0]++