此表达式在 C 中的计算结果为 true 还是 false(1 或 0)?

Will this expression evaluate to true or false (1 or 0) in C?

提问人:user2277550 提问时间:1/11/2023 最后编辑:Vlad from Moscowuser2277550 更新时间:10/26/2023 访问量:188

问:

#include<stdio.h>
int main()
{
    int a=4;
    int b=4;
    int c= a++ < ++b? 1 : 0;
    
    printf ("%d",c);
}

已知 处有一个序列点,这意味着前缀和后缀操作都必须在该点之前完成。此外,已知(?)在比较之前递增。但是,在比较之前还是之后递增??ba

如果它在测试之前递增,则布尔值的计算结果为 false 并设置为 0,否则为 true,设置为 1。在我的编译器中,它的计算结果为 true,这意味着在设置为 1 的比较操作之后执行。<cca++c

但是,这种行为是规范的一部分吗?

我把它修改成

#include<stdio.h>
int main()
{
    int a=4;
    int b=4;
    int d=2;
    int c= a++ + d < ++b + d? 1 : 0;
    
    printf ("%d",c);
}

它的计算结果仍为 1。后缀必须在 之前完成,但这真的能确保它在比较之后发生吗??<

c 一元 序列点 后缀运算符

评论

2赞 Weather Vane 1/11/2023
a在比较后递增。后递增,因此使用未递增的值。
3赞 Andrew Henle 1/11/2023
int c= a++ < ++b? 1 : 0;可以直接替换为 ,尽管我可能会用它来使将布尔比较结果分配给 clear 的意图。int c= a++ < ++b;int c= (a++ < ++b);c
1赞 John Bollinger 1/12/2023
在评估比较之前或之后修改存储值与比较结果无关。重要的是表达式的值,它是增量之前的值,而不管存储的值何时相对于其他程序行为进行修改。aa++aa

答:

9赞 Chris 1/11/2023 #1

a++返回增量之前的值。 返回 after the increment 的值。因此,这减少到哪个减少到哪个为真,因此表达式的计算结果为 。a++bb4 + 2 < 5 + 26 < 7a++ + d < ++b + d ? 1 : 01

由于表达式中既没有使用,也没有多次使用,因此不存在未定义的行为。ab

评论

0赞 user2277550 1/12/2023
嗨,您能为我指出一些有关后缀运算符的正式规则的资源吗?
1赞 Weather Vane 1/12/2023
@user2277550相关:C 中的序列点。后增量发生在正在读取的值和下一个序列点之间的某个未指定时刻。
0赞 user2277550 1/12/2023
@WeatherVane 但是那个序列点是“?”,对吧?
1赞 Weather Vane 1/12/2023
这篇关于序列点的维基百科文章说 2.在三元条件运算符的第一个操作数的计算与其第二个或第三个操作数之间。例如,在表达式 a = (*p++) ?(*p++) : 0 在第一个 *p++ 之后有一个序列点,这意味着在执行第二个实例时它已经递增。但是,在读取您的之前,它仍然不会发生:这将是预增量。c
1赞 Steve Summit 1/12/2023
@user2277550 请担心序列点,即更新的值将存储到 和 中,以及何时存储。但是序列点与运算符看到的值无关。就运算符而言,表达式也可以是 。另请参阅我的答案ab<<c = a + d < (b+1) + d? 1 : 0
6赞 Steve Summit 1/12/2023 #2

此外,已知 (?) b 在比较之前递增。 但是,在比较之前还是之后递增?

这是一个微妙的点,但重要的是要了解这里到底发生了什么。

两个子表达式 和 做件事。它们计算要在周围表达式中使用的新值,更新它们正在操作的变量的存储值。a++++b

这也是这样做的:a++

  1. 它将 (4) 的旧值输出到周围的表达式中a
  2. 它将一个新值 (5) 存储到 中。a

并这样做:++b

  1. 它将 (4+1 或 5) 的新值输出到周围的表达式中b
  2. 它将一个新值 (5) 存储到 中。b

请注意,在这两种情况下,操作员关心的是事情 1。而且,在这两种情况下,事物 1 都是一个绝对的定义,它不依赖于时间。<

或者,换句话说,问“/是在比较之前还是之后递增的?”并不是真正的正确问题。价值观并参与比较,仅此而已。abab+1

时机是事情 2.我们不知道,确切地说,新值何时被存储回 .我们也不知道新值何时被存储回 。我们所知道的是,这些存储将发生在下一个序列点之前的某个时间(正如您正确指出的,在本例中是三元运算符的一部分)。ab?

但没有什么取决于这些时间,所以这里没有未定义的行为。

当以下任一情况发生时,会出现未定义的行为

  1. 被修改的变量 ( 或 ) 在表达式中的其他位置也有其值独立使用,这意味着我们不知道该用法是使用旧值还是新值ab
  2. 同一个变量被修改了两次,这意味着我们不知道两次修改中的哪一个“胜”

但是,同样,这些问题在这里都没有发生,因此表达式是明确定义的。

评论

1赞 John Bollinger 1/12/2023
这。正是这个。
0赞 Vlad from Moscow 1/12/2023 #3

来自 C 标准(6.5.2.4 后缀递增和递减运算符)

2 postfix ++ 运算符的结果是操作数的值。 作为副作用,操作数对象的值会递增(即 是,将相应类型的值 1 添加到其中)。

所以在这个宣言中

int c= a++ < ++b? 1 : 0;

初始值设定项中使用的子表达式的值是操作数在其增量之前的值。a++a

另一方面(C 标准(6.5.3.1 前缀递增和递减运算符))

2 前缀 ++ 运算符的操作数值递增。结果是递增后操作数的新值

所以子表达式的值就是递增后的值。++bb

因此,您实际上拥有

int c = 4 < 5 ? 1 : 0;

至于序列点,那么为了演示它,你可以写例如

int c = a++ < b++ ? a : b;

在这种情况下,变量在应用递增的副作用后将具有变量的值,即 。cb5