同时修改指向值和指针UB

Is modifying the pointed value and the pointer at the same time UB

本文关键字:指针 UB 修改      更新时间:2023-10-16

我知道C和c++和不同的语言,但以下内容适用于两者。

TL博士/

我知道i = i++;是UB,因为I在表达式中被修改了两次,而C和c++禁止它。

引用:

C99 6.5:

如果标量对象上的副作用相对于另一个副作用是无序的在同一标量对象上,或使用同一标量的值进行值计算对象时,行为未定义。如果有多个允许的排序如果是表达式的子表达式,则如果有这样的未排序的一侧,则该行为是未定义的

c++ 11 - 1.9 15:

如果是标量的副作用对象相对于同一标量对象或值计算上的另一个副作用是无序的使用相同标量对象的值,并且它们不是潜在的并发,则行为是定义。

所以我理解*i = *i++ + *j++;导致UB,因为i的后增量和对*i的影响可能是未排序的,CLang在C或c++模式下发出警告:警告:未排序的修改和访问' I ' [- unsequenced] *i = *i++ + *j++;

但是我不理解*i++ = *i + *j++;上同样的警告。因为在这里,我们首先计算正确的部分,影响它,并在影响后增加。

两种语言的规范都说(同样的段落,就在上面):

操作符操作数的值计算在运算符

的结果的值计算之前排序。

结束TL博士/

所以问题是:

这行是

*i++ = *i + *j++;

未定义的行为,还是Clang(版本3.4.1)在发出警告时过于保守?

原因

*i = *i++ + *j++;

*i++ = *i + *j++;

是未定义的是,你试图在一个表达式中使用指针i,这个表达式是一个值计算(解引用,*i)和一个有副作用的表达式(解引用和递增,*i++),没有中间的序列点。记住*i++被计算为*(i++);你在增加指针的值,而不是被指向的东西。

给定x = *i + *i++;,将子表达式i++分解为七个部分是合理的:

  1. 捕获表达式中*i++部分的指针状态
  2. 读取地址
  3. 捕获表达式*i部分指针的状态
  4. 读取地址
  5. 添加读取的两个值
  6. 将结果存储在x中。
  7. 将指针置于无效状态(例如,对于大于int的指针,从下半部分开始增加,这可能会换行)
  8. 将指针置于标识下一个对象的有效状态(完成写入更新的指针值)

步骤#1必须在第一个,并且必须在#7之前,而#7又必须在#8之前,但是编译器可以重新排列#2-#6的任何或全部操作,使它们出现在#7和#8之前,之后或之间。标准中没有要求编译器做出任何努力来确保对同一指针的所有其他访问都发生在#7之前或#8之后;如果编译器碰巧在步骤#7和#8之间放置了不同的访问,则无法告诉使用临时无效指针的后果可能是什么。