【问题标题】:Is "return i++, i, i++;" undefined behaviour in C?是“返回 i++, i, i++;” C中未定义的行为?
【发布时间】:2017-11-13 12:20:35
【问题描述】:

如果我这样写:

i = i++, i, i++;

这是 C 语言中的未定义行为

但是,如果我这样写:

return i++, i, i++; // Is it UB?

这是未定义的行为吗?

示例:

#include <stdio.h>

int f(int i)
{
    return i++, i, i++; // Is it UB?
}
int main() {
    int i = 1;
    i = i++, i, i++;

    i = f(i);
    printf("%d\n",i);
    return 0;
}

【问题讨论】:

  • @StoryTeller - =, 绑定得更紧密,所以我相信这相当于 (i = i++), i, i++;
  • @OliverCharlesworth - 是的,退货声明让我失望了。这是一条红鲱鱼
  • @OliverCharlesworth - Unwind 的回答比 C11 大 3 岁。鉴于上述措辞,UB现在如何?左侧没有i修改。没有 to 序列。 i++ 的值计算是在赋值之前完成的,反正按照 n1570。那么还有什么未定义的呢?
  • @StoryTeller C11 没有改变这一点,它仍然是 UB。然而,我相信 C++11 确实改变了规则。
  • @Lundin - 是的,你一直它是 UB。但是考虑到措辞,我想了解为什么它是 UB,即使一切看起来都已签出。

标签: c language-lawyer undefined-behavior c11


【解决方案1】:

i = i++, i, i++; 是 UB,因为 = 的优先级高于 ,,因此表达式被解析为 (i = i++), i, i++,其中子表达式 i = i++ 调用 UB1)

即使代码写成i = (i++, i, i++);,它仍然是UB,因为现在最右边的i++i之间没有序列点,左边= 的操作数 1).

但是,当您删除 i = 部分时,您会删除该模棱两可的行为。 return i++, i, i++; 必须排序为:

i++, the left one
sequence point from left comma
i
sequence point from right comma
i++, the right one
sequence point before returning

所以它是明确定义的。


来源:

1) C11 6.5/2

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

还有 C11 6.5.16/3

更新左操作数的存储值的副作用是 在左右操作数的值计算之后排序。操作数的计算是无序的。

值得注意的是,上面关于赋值的文字在 C11 和 C++11 之间是不同的。

【讨论】:

  • 但是非常糟糕的代码。根据经验,任何将++ 与同一表达式中的其他操作混合的代码都是可疑的。很少有理由将++ 与其他操作混合使用,在 99% 的情况下,这只是一种草率、危险的风格。
  • 抱歉吹毛求疵,但我认为您想在“您删除明确的行为”中删除“un”。
  • 那么i = (i++, i++, i) 是定义的行为吗?同上i = i, i++, i++?
  • @TLW 否,在i = (i++, i++, i) 的情况下,两个子表达式i(i++, i++, i) 没有排序。这意味着i 可以首先被评估,然后紧接着最左边的i++。所以理论上它仍然是 UB(在 C 中,在 C++11 中可以)。在i = i, i++, i++ 的情况下,它是明确定义的。
  • @Lundin:我知道排序并不总是可传递的,但 i=(i++,i) 似乎是一个奇怪的地方,因为它是不可传递的。标准认识到非复合赋值可能会在评估右侧之前确定 what 左值正在被修改(例如,在p[i] 等情况下),但必须表现得好像它们不是从左侧的左值,并且在所有使用右侧相同的左值的读取完成之前不要写入它的任何部分。在某些情况下,例如 long long i; ... i=i&gt;&gt;1i*=3 编译器可能能够编写 i 的一半...
【解决方案2】:
return i++, i, i++;

不,这不是未定义的行为,因为逗号运算符定义了一个序列点,它将对i 的修改访问分开。

因此这段代码等价于:

i++;        // The left-most subexpression
i;
return i++; // The right-most subexpression

比较:

i = i++, i, i++;

这个未定义的行为,因为, 的优先级低于=,所以它相当于:

(i = i++), i, i++;

i = i++ 是未定义的行为(有关此特殊情况的更多信息,请参阅this 问题)。

【讨论】:

  • 因此f 等价于{ return i+1; }
猜你喜欢
  • 2017-02-15
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
相关资源
最近更新 更多