【问题标题】:Is this "*ptr++ = *ptr + a" undefined behavior?这是“*ptr++ = *ptr + a”未定义的行为吗?
【发布时间】:2010-12-22 07:48:39
【问题描述】:

好吧,我并不是真的需要这个答案,我只是好奇。

*ptr++ = a 这样的表达式是完全有效的,因为我们在两个对象上操作ptr*ptr 但是如果我写*ptr++ = *ptr + a 它仍然有效吗?

例如考虑以下 sn-p:

int main(void){
   int a[] = {5,7,8,9,2};

   int* p =a;

   *p++ = 76; /*altering the first element */
   *p++ = *p + 32; /*altering the second element */    

   p = a;
   int i;
   for(i = 0;i<5; i++)
      printf("%d ",*p++);

   return 0;
}

我认为表达式*p++ = *p + 32; 没什么好担心的,但我不确定所涉及的序列点。

【问题讨论】:

  • 为什么不 *p++ += 32; ?实际上,这对我来说有点奇怪。 :-)
  • 没有 p 的声明,它非常重要。
  • @MSalters:人们会假设 OP 出于某种原因定义了“a”。并且有一个分配'p = a'。所以这意味着 p 是一个 int。 (否则这个问题无论如何都没那么有趣)。
  • @Debanjan:是的,有很多事情要担心。 :-)

标签: c++ c sequence-points


【解决方案1】:

就 C 而言,*ptr++ = *ptr + 32 在 6.5.2 中未定义:“如果标量对象的副作用相对于同一标量对象的不同副作用或使用相同的标量对象,行为未定义。”您正在修改并尝试在另一个计算中使用 ptr 的值,而没有中间的序列点。

【讨论】:

    【解决方案2】:

    首先让我们假设'p'是一个指针类型。
    否则所有的操作都只是函数调用的语法糖。

    让我们把陈述分解成几部分。

    int* p = a;
    
    *p++ = *p + 32;
    
    << Sequence Point >>
    // Part 1: p++
    // Note the definition of post increment in the standard is (5.2.6)
    // The result of the expression p++ is the value of 'p' while the value of the 
    // objects represented by 'p' is incremented. This can be represented in pseudo code as:
    (A) int*  p1 = p;
    (B) p = p + 1;
    
    // Part 2: *p (On the result of Part 1) (On *p++)
    (C) int& p2 = *p1;  // Note the use of p1;
    
    // Part 3: *p (On *p + 32)
    // Note: There is no linkage between this use of 'p' and the 'p' in Part 1&2
    (D) int& p3 = *p;
    
    // Part 4: *p + 32;
    (E) int p5 = p3 + 32; // Note the use of p3;
    
    // Part 5: Assignment.
    (F) p2 = p5;
    << Sequence Point >>
    
    Ordering that must be preserved:
    (A) Before (B)
    (A) Before (C)
    (D) Before (E)
    (C) Before (F)
    (E) Before (F)
    

    鉴于上述限制:
    编译器可以通过多种方式重新排序这些指令,
    但要注意的要点是 (B) 可以发生在任何地方,对 (B) 的唯一限制是它发生在 (A) 之后。因此 (D) 中定义的 p3 的值可能是两个不同值之一,具体取决于(B) 的确切位置。

    因为这里不能定义 p3 的值。
    结果语句具有未定义的行为。

    【讨论】:

      【解决方案3】:

      *ptr++ = *ptr + a 的结果未定义。等号不是序列点,因此在该语句中再次使用 ptr 的值会导致未定义的行为。只需考虑 ptr 在 RHS 表达式计算之前递增的结果,并将其与 ptr 在 RHS 表达式之后递增的情况进行比较。

      注意:这并不是说表达式的结果将来自这两种情况中的任何一种。未定义就是未定义。

      基本上,您只能指望两件事:在最后一个序列点和下一个序列点之间的某个时间计算后增量,以及表达式 ptr++ 返回 ptr 的值 before 它是递增的。所以*ptr++ = a 很好,因为你可以依靠ptr++ 的结果。

      【讨论】:

      • 它被定义为在*ptr上加a,并使ptr指向下一个元素。唯一的问题是是否有下一个元素。
      • No.. 这个想法是你不知道ptr 的值是什么,因为你已经在当前序列点对之间的其他地方增加了它。
      • operator= 是一个函数调用和一个序列点(因为它 operator*operator+)但很可能 ptr 指向一个内置类型,在这种情况下取​​消引用, 加法和赋值不是运算符,不是函数调用,也不是序列点。
      • 其实.. 我认为变化不大。函数调用是一个序列点,但函数参数的求值顺序是任意的。所以他的例子仍然是不确定的。我实际上认为重载运算符与内置运算符具有完全相同的未定义行为..
      • 除了赋值副作用本身(这会发生在operator= 内部,因此会受到保护)。但是,左侧的ptr 的增量和右侧对ptr 的访问是未定义的行为(因为增量不是从右侧的访问中获取其值,而是独立操作),即使对于用户定义的运算符函数(因为它们是函数参数,就像你说的那样)。
      【解决方案4】:

      如果你问的是不一样的。 它会编译...

      这是合法的 - 如果 PTR 指向一个数组,而不是该数组的最后一个单元格, 所以递增 PTR 将指向数组中的下一个对象。

      *ptr++ = *ptr + 2 等同于 *ptr = *ptr + 2, ptr++

      【讨论】:

      • 我相信他/她在问是否在 C/C++ 标准中定义了 = 右侧的 *ptr 是指向 ++ 之前还是之后的字符。
      • *ptr++ = a; 有什么问题?这是完全合法的。
      • @ysth:嗯,我是他,&我不明白你在问什么;)
      【解决方案5】:

      我认为它是未定义的。不过我不确定。

      但是,有点更广泛的风格点:如果一个表达式让你开始怀疑它是否未定义,这可能表明你的代码不清楚它的意图,需要减少歧义,减少非定义明显依赖于评估顺序。

      我曾经认为 C 真的很酷,因为你可以写很多非常简短的语句来做很多疯狂的事情。我不再这么想了。 :-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-21
        • 1970-01-01
        • 2019-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-28
        • 1970-01-01
        相关资源
        最近更新 更多