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

C 是(x+;+;,y)+;(y+;+;,x)未定义或未指定,如果未指定,它可以计算什么?,c,c99,language-lawyer,c11,unspecified-behavior,C,C99,Language Lawyer,C11,Unspecified Behavior,在表达式中引入。我想知道这是否意味着下面的程序避免了未定义的行为 int x, y; int main() { return (x++, y) + (y++, x); } 如果它确实避免了未定义的行为,它仍然可能是未指定的,也就是说,返回几个可能的值之一。我认为在C99中,它只能计算1,但实际上,各种版本的GCC将该程序编译成返回2的可执行文件。Clang生成一个返回1的可执行文件,显然符合我的直觉 最后,这在C11中是否发生了变化?我的理解是,以下任何评估命令都是合法的: x++,y

在表达式中引入。我想知道这是否意味着下面的程序避免了未定义的行为

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
如果它确实避免了未定义的行为,它仍然可能是未指定的,也就是说,返回几个可能的值之一。我认为在C99中,它只能计算
1
,但实际上,各种版本的GCC将该程序编译成返回
2
的可执行文件。Clang生成一个返回
1
的可执行文件,显然符合我的直觉


最后,这在C11中是否发生了变化?

我的理解是,以下任何评估命令都是合法的:

  • x++,y++,y,x
  • x++,y,y++,x
  • x++,y++,x,y
  • y++,x,x++,y
但不是:

  • y,x++,x,y++
据我所知,唯一需要的排序是
(x++)
必须在
(y)
之前,并且
(y++)
必须在
(x)
之前


因此,我认为这仍然是未定义的行为。

我的理解是,以下任何评估命令都是合法的:

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
  • x++,y++,y,x
  • x++,y,y++,x
  • x++,y++,x,y
  • y++,x,x++,y
但不是:

  • y,x++,x,y++
据我所知,唯一需要的排序是
(x++)
必须在
(y)
之前,并且
(y++)
必须在
(x)
之前

因此,我认为这仍然是未定义的行为。

采用以下表达式:

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
(x++, y) + (y++, x)
从左到右求值:

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
标准中没有禁止这一点,所以整件事都有未定义的行为

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
对比此伪代码:

f() { return x++, y; }
g() { return y++, x; }
f() + g()
a根据C99(5.1.2.3/2),对
f
g
的调用本身被视为副作用,函数调用操作符在进入函数之前包含一个序列点。这意味着函数执行不能交错

在“并行评估”模式下:

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

采用以下表达式:

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
(x++, y) + (y++, x)
从左到右求值:

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
标准中没有禁止这一点,所以整件事都有未定义的行为

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
对比此伪代码:

f() { return x++, y; }
g() { return y++, x; }
f() + g()
a根据C99(5.1.2.3/2),对
f
g
的调用本身被视为副作用,函数调用操作符在进入函数之前包含一个序列点。这意味着函数执行不能交错

在“并行评估”模式下:


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

整个标准第6.5章都提到了基于操作员的评估顺序。我能找到的关于评估顺序的最佳总结是标准的(非规范性)附录J:

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}
C11附件J

J.1未指明的行为

  • 子表达式的求值顺序和副作用发生的顺序,除非为 函数调用()、&&、| |、?:、和逗号运算符(6.5)
在您的示例中,您无法知道是先计算子表达式
(x++,y)
还是
(y++,x)
,因为+运算符的操作数的计算顺序未指定

至于未定义的行为,逗号运算符没有解决任何问题。如果首先计算
(x++,y)
,则可以在另一个子表达式的
y++
之前立即计算
y
。由于y被访问两次,其间没有一个序列点,因此除了用于确定要存储的值之外,该行为是未定义的

因此,您的程序同时具有未定义和未指定的行为


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

整个标准第6.5章都提到了基于操作员的评估顺序。我能找到的关于评估顺序的最佳总结是标准的(非规范性)附录J:

C11附件J

J.1未指明的行为

  • 子表达式的求值顺序和副作用发生的顺序,除非为 函数调用()、&&、| |、?:、和逗号运算符(6.5)
在您的示例中,您无法知道是先计算子表达式
(x++,y)
还是
(y++,x)
,因为+运算符的操作数的计算顺序未指定

至于未定义的行为,逗号运算符没有解决任何问题。如果首先计算
(x++,y)
,则可以在另一个子表达式的
y++
之前立即计算
y
。由于y被访问两次,其间没有一个序列点,因此除了用于确定要存储的值之外,该行为是未定义的

因此,您的程序同时具有未定义和未指定的行为


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

该标准明确声明了哪些操作符引入序列点,
+
不在其中。所以你的表达可以是evalu