在UB同时修改指向值和指针

时间:2015-05-21 16:42:01

标签: c++ c language-lawyer undefined-behavior

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

TL / DR

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

参考文献:

  

C99 6.5:

     

如果相对于不同的副作用,对标量对象的副作用未被排序   在相同的标量对象上或使用相同标量的值进行值计算   对象,行为未定义。如果有多个允许的排序   表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的   效果发生在任何排序

     

C ++ 11 - 1.9 15:

     

如果对标量有副作用   对于相同标量对象的另一个副作用或值计算,对象未被排序   使用相同标量对象的值,并且它们不可能并发,行为是   未定义。

所以我理解*i = *i++ + *j++;会导致UB,因为i上的后增量和对*i的影响可能没有排序,并且CLang在C或C ++模式下发出警告:警告:未经测试的修改和访问'i'[-Wunsequenced] *i = *i++ + *j++;

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

两种语言的规格都说(同一段,上面):

  

运算符的操作数的值计算   在运算符结果的值计算之前进行排序

END TL / DR

所以问题是:

这一行

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

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

2 个答案:

答案 0 :(得分:2)

两个原因

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

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

未定义的是您尝试在表达式中使用指针i,该表达式是一个值计算(取消引用,*i)和一个带副作用的表达式(取消引用和增量, *i++)没有插入序列点。请记住,*i++评估为*(i++);你正在递增指针值,而不是指向的东西。

答案 1 :(得分:1)

鉴于x = *i + *i++;,将子表达式i++分解为七个部分是合法的:

  1. 捕获表达式
  2. 阅读该地址
  3. 捕获表达式的的指针状态
  4. 阅读该地址
  5. 添加两个读取值
  6. 将结果存储在X中。
  7. 将指针置于无效状态(例如,对于大于int的指针,首先递增下半部分,这可能会换行)
  8. 将指针置于标识下一个对象的有效状态(完成写入更新的指针值)
  9. 步骤#1必须是第一个,并且必须在#7之前,而#7必须先于#8,但编译器可以重新排列任何或所有操作#2-#6,以便它们在#之前,之后或之间发生# 7和#8。标准中的任何内容都不需要编译器做出任何努力来确保对#17之前或#8之后的同一指针的所有其他访问;如果编译器碰巧在步骤#7和#8之间进行了不同的访问,则无法确定使用临时无效指针的后果是什么。