C 语句int val=(+;+;i>;+;+;j)是否正确++;i:++;j、 `调用未定义的行为?

C 语句int val=(+;+;i>;+;+;j)是否正确++;i:++;j、 `调用未定义的行为?,c,ternary-operator,sequence-points,C,Ternary Operator,Sequence Points,鉴于以下计划: #include <stdio.h> int main(void) { int i = 1, j = 2; int val = (++i > ++j) ? ++i : ++j; printf("%d\n", val); // prints 4 return 0; } #包括 内部主(空) { int i=1,j=2; int val=(++i>++j)?++i:++j; printf(“%d\n”,val);//打印4 返回0;

鉴于以下计划:

#include <stdio.h>
int main(void)
{
    int i = 1, j = 2;
    int val = (++i > ++j) ? ++i : ++j;
    printf("%d\n", val); // prints 4
    return 0;
}
#包括
内部主(空)
{
int i=1,j=2;
int val=(++i>++j)?++i:++j;
printf(“%d\n”,val);//打印4
返回0;
}
val
的初始化似乎隐藏了一些未定义的行为,但我没有看到任何一个点,其中一个对象被多次修改,或者被修改并使用时中间没有一个序列点。有人可以吗
是否正确或证实我的观点?

此代码的行为定义良好

条件中的第一个表达式保证在第二个表达式或第三个表达式之前求值,并且只求值第二个或第三个表达式中的一个。本手册第6.5.15p4节对此进行了说明:

计算第一个操作数;有一个序列点 在其评估和第二或第三个评估之间 操作数(以计算值为准)。计算第二个操作数 仅当第一个值不等于0时;第三个操作数是 仅当第一个比较值等于0时才计算;结果是 第二个或第三个操作数的值(以较低者为准) 已评估),转换为下面描述的类型

对于您的表达式:

int val = (++i > ++j) ? ++i : ++j;

首先计算
++i>++j
。比较中使用了
i
j
的增量值,因此它变为
2>3
。结果为假,因此计算
++j
,而不计算
++i
。因此,
j
(即4)的(再次)增量值随后被分配给
val

,虽然太晚了,但可能有用

(++i > ++j) ? ++i : ++j;
在文件中,我们发现有一个序列点

在条件?:运算符的第一个操作数求值与第二个和第三个操作数求值之间

为了更好地定义行为,不得在两个序列点之间修改同一对象2次(通过副作用)

在您的表达式中,唯一可能出现的冲突是第一个和第二个
++i
++j
之间的冲突

在每个序列点上,最后存储在对象中的值应与抽象机器规定的值一致(这是您将在纸上计算的值,就像在图灵机器上一样)

引用自
5.1.2.3p3程序执行

表达式a和B的计算之间存在一个序列点,这意味着与a相关的每个值计算和副作用都是在与B相关的每个值计算和副作用之前排序的

当代码中有副作用时,它们会按不同的表达式排序。规则规定,在两个序列点之间,可以根据需要排列这些表达式

比如说<代码>i=i++。由于此表达式中涉及的运算符都不表示序列点,因此可以根据需要排列具有副作用的表达式。C语言允许您使用这些序列中的任何一种


i=i;i=i+1
i=i+1;i=i
tmp=i;i=i+1;i=tmp
tmp=i;i=tmp;i=i+1
或提供与相同结果的任何内容要求解释此计算。标准ISO9899将C语言定义为抽象语义。

您的程序中可能没有UB,但问题是: 语句是否为
intval=(++i>++j)++i:++j调用未定义的行为

答案是肯定的。由于
i
j
已签名,因此增量操作中的一个或两个操作都可能溢出,在这种情况下,所有下注都将关闭


当然,在您的完整示例中不会出现这种情况,因为您已将值指定为小整数。

我将在@Doug Currie上评论有符号整数溢出有点牵强,尽管从技术上来说答案是正确的。恰恰相反

再想一想,我认为Doug的答案不仅是正确的,而且假设一个不完全是简单的三行程序(但是一个可能有一个循环之类的程序)应该扩展到一个明确的“是”。原因如下:

编译器看到
inti=1,j=2
,因此它知道++i将等于
j
,因此不可能大于
j
,甚至不可能大于
++j
。现代优化器可以看到这些琐碎的事情

当然,除非其中一个溢出。但是优化器知道这将是UB,因此假设它永远不会发生,并根据UB进行优化

因此,三元运算符的条件总是错误的(在这个简单的例子中肯定是错误的,但即使在循环中重复调用,情况也是如此!),
i
只会递增一次,而
j
总是递增两次。因此,不仅
j
总是大于
i
,它甚至在每次迭代中都会增加(直到溢出发生,但根据我们的假设,这永远不会发生)

因此,优化器可以将其转换为
++i;j+=2无条件,这肯定不是人们所期望的


这同样适用于具有未知值
i
j
的循环,例如用户提供的输入。优化器可能非常清楚地认识到,操作序列只取决于
i
j
的初始值。因此,可以通过复制循环(每种情况一次)并使用单个
if(i>j)
在这两个循环之间切换来优化跟随条件移动的增量序列。然后,当我们这样做的时候,它可能会把重复递增的循环折叠成类似于
(j-i)的东西,有序列点吗?请看有哪些州