是(x ++,y)+(y ++,x)未定义或未指定,如果未指定,它可以计算什么?

Pas*_*uoq 1 c c99 language-lawyer unspecified-behavior c11

逗号序列操作者引入了一个顺序点的表达式中.我想知道这是否意味着下面的程序避免了未定义的行为.

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
Run Code Online (Sandbox Code Playgroud)

如果它确实避免了未定义的行为,它仍然可能未指定,即返回几个可能值中的一个.我认为在C99中,它只能计算1,但实际上,各种版本的GCC将此程序编译成可返回的可执行文件2.Clang生成一个返回的可执行文件1,显然与我的直觉一致.

最后,这是C11改变了吗?

mel*_*ene 6

拿表达式:

(x++, y) + (y++, x)
Run Code Online (Sandbox Code Playgroud)

从左到右评估:

x++  // yield 0, schedule increment of x
,    // sequence point: x definitely incremented now
y    // yield 0
y++  // yield 0, schedule increment of y
// explode because you just read from y and wrote to y
// with no intervening sequence point
Run Code Online (Sandbox Code Playgroud)

标准中没有任何内容禁止这样做,因此整个事情都有未定义的行为.

对比这个伪代码:

f() { return x++, y; }
g() { return y++, x; }
f() + g()
Run Code Online (Sandbox Code Playgroud)

根据C99(5.1.2.3/2),调用fg自身计为副作用,函数调用运算符在进入函数之前包含一个序列点.这意味着函数执行不能交错.

在"并行评估"模型下:

f()  // arbitrarily start with f: sequence point; enter f
g()  // at the same time, start calling g: sequence point
Run Code Online (Sandbox Code Playgroud)

由于f计数作为副作用本身g()执行,序列点暂停执行直到f返回.因此,没有未定义的行为.


Lun*_*din 5

标准的第6.5章提到了运营商的评估顺序.我能找到的评估顺序的最佳总结是该标准的(非规范性)附件J:

C11附件J.

J.1未指定的行为

  • 除了为函数调用(),&&,||,?:和逗号运算符(6.5)指定的情况外,评估子表达式的顺序和副作用的顺序除外

在你的榜样,你无法知道是否有子表达式(x++, y)(y++, x)先评估,因为+运算符的操作数的计算顺序是不确定的.

至于未定义的行为,逗号运算符什么也没解决.如果(x++, y)首先进行评估,则y可以y++在另一个子表达式之前立即进行评估.由于y被访问两次而中间没有序列点,出于其他目的而不是确定要存储的值,行为是未定义的.更多信息在这里.

所以你的程序都有未定义和未指定的行为.

(此外,它具有实现定义的行为,因为int main(),而不是int main(void)不是托管应用程序中明确定义的main版本之一.)