在语句中多次使用时 C++ 中的前缀运算符行为
prefix operator behaviour in c++ when used multiple times in a statement
我无法理解前缀运算符在语句中多次使用时的行为。
这是一个示例代码来说明我的问题
#include<iostream>
using namespace std;
int main()
{ int a=2,adup=2;
int b = (++a) * (++a);
cout<<endl<<"square of "<<a<<" is "<<b<<endl;//i get 16
int c = (++adup) * (++adup) * (++adup);
cout<<endl<<"adup is "<<adup<<" and c is "<<c<<endl;// i get 80
return 0;
}
将以上文件另存为 code.cpp 和 运行 'gcc code.cpp -lstdc++' 进行验证。
似乎在第一种情况下 2 增加了两次然后 b = 4x4
但在第二种情况下,我希望 5x5x5 在上面的行中,但我们得到 4x4x5 。这怎么解释呢?
谢谢
顺便说一句,我发现 postfix 运算符似乎合乎逻辑。它只是更新值并在下次使用变量时使用它,即使在同一语句中也是如此。
阅读答案后我的结论是 -
是否可以说在语句中对同一变量多次使用 pre 或 post 操作将是未定义的行为,而不管表达式 LHS 上的任何内容?
C++ 的早期迭代(以及与此相关的 C)具有序列点的概念,它保证所有先前的“事物”(例如计算该表达式的 i++
副作用)都已经完成.
这在某些时候被更严格描述的“排序”所取代:
- 如果
A
在 B
之前排序,A
将在 B
开始之前完成。
- 如果
A
在 B
之后排序,A
将不会开始,直到 B
完成。
- 如果
A
和 B
的顺序不确定,一个将在另一个开始之前完全完成,但您不一定知道哪个先完成。
- 如果
A
和 B
是无序的,它们可能先开始,甚至可能重叠。
从 C++20 开始,[intro.execution]
,第 10 点适用于您的代码:
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
这意味着完整表达式的各个位可能以任何顺序出现,甚至重叠,导致结果与您认为正确的结果不同。
换句话说,如果您想要确定性代码,请不要那样做。
而且,尽管您发现“后缀运算符看起来合乎逻辑,但它只是更新 [the] 值并在下次使用 [the] 变量时使用它,即使在同一语句中”,这实际上是不正确的。
虽然在特定情况下可能会发生这种情况,但表达式 (a++) * (a++)
等同于 as non-determinstic as (++a) * (++a)
.
如果您想保证操作的顺序,您可以使用更明确的代码,例如:
// int b = (++a) * (++a);
// with pre-increment immediately before EACH use of a:
int b = (a + 1) * (a + 2); a += 2;
// or:
int b = ++a; b = ++a * b
总有一些方法可以用 不会 受不确定结果影响的代码实现相同的效果,如果您关心这种情况,则代码行数相同公制,尽管在一行上有两个 语句 :-)
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
Example [
int i = 7;
i = i++ + i; //undefined behavior
]
出于同样的原因,您的程序具有 未定义的行为。
How can that be explained ?
未定义的行为 意味着任何事情1 都可能发生包括但不限于 程序给出你的预期输出。但是从不依赖(或根据)具有未定义行为的程序的输出。
所以您看到(也许看到)的输出是未定义行为的结果。正如我所说,不要依赖具有 UB 的程序的输出。程序可能会崩溃。
因此,使程序正确的第一步是删除 UB。 然后并且只有那时你可以开始对程序的输出进行推理。
1有关未定义行为的更准确的技术定义,请参阅 this 其中提到:没有对程序行为的限制.
我无法理解前缀运算符在语句中多次使用时的行为。
这是一个示例代码来说明我的问题
#include<iostream>
using namespace std;
int main()
{ int a=2,adup=2;
int b = (++a) * (++a);
cout<<endl<<"square of "<<a<<" is "<<b<<endl;//i get 16
int c = (++adup) * (++adup) * (++adup);
cout<<endl<<"adup is "<<adup<<" and c is "<<c<<endl;// i get 80
return 0;
}
将以上文件另存为 code.cpp 和 运行 'gcc code.cpp -lstdc++' 进行验证。
似乎在第一种情况下 2 增加了两次然后 b = 4x4
但在第二种情况下,我希望 5x5x5 在上面的行中,但我们得到 4x4x5 。这怎么解释呢?
谢谢
顺便说一句,我发现 postfix 运算符似乎合乎逻辑。它只是更新值并在下次使用变量时使用它,即使在同一语句中也是如此。
阅读答案后我的结论是 - 是否可以说在语句中对同一变量多次使用 pre 或 post 操作将是未定义的行为,而不管表达式 LHS 上的任何内容?
C++ 的早期迭代(以及与此相关的 C)具有序列点的概念,它保证所有先前的“事物”(例如计算该表达式的 i++
副作用)都已经完成.
这在某些时候被更严格描述的“排序”所取代:
- 如果
A
在B
之前排序,A
将在B
开始之前完成。 - 如果
A
在B
之后排序,A
将不会开始,直到B
完成。 - 如果
A
和B
的顺序不确定,一个将在另一个开始之前完全完成,但您不一定知道哪个先完成。 - 如果
A
和B
是无序的,它们可能先开始,甚至可能重叠。
从 C++20 开始,[intro.execution]
,第 10 点适用于您的代码:
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
这意味着完整表达式的各个位可能以任何顺序出现,甚至重叠,导致结果与您认为正确的结果不同。
换句话说,如果您想要确定性代码,请不要那样做。
而且,尽管您发现“后缀运算符看起来合乎逻辑,但它只是更新 [the] 值并在下次使用 [the] 变量时使用它,即使在同一语句中”,这实际上是不正确的。
虽然在特定情况下可能会发生这种情况,但表达式 (a++) * (a++)
等同于 as non-determinstic as (++a) * (++a)
.
如果您想保证操作的顺序,您可以使用更明确的代码,例如:
// int b = (++a) * (++a);
// with pre-increment immediately before EACH use of a:
int b = (a + 1) * (a + 2); a += 2;
// or:
int b = ++a; b = ++a * b
总有一些方法可以用 不会 受不确定结果影响的代码实现相同的效果,如果您关心这种情况,则代码行数相同公制,尽管在一行上有两个 语句 :-)
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
Example [
int i = 7;
i = i++ + i; //undefined behavior
]
出于同样的原因,您的程序具有 未定义的行为。
How can that be explained ?
未定义的行为 意味着任何事情1 都可能发生包括但不限于 程序给出你的预期输出。但是从不依赖(或根据)具有未定义行为的程序的输出。
所以您看到(也许看到)的输出是未定义行为的结果。正如我所说,不要依赖具有 UB 的程序的输出。程序可能会崩溃。
因此,使程序正确的第一步是删除 UB。 然后并且只有那时你可以开始对程序的输出进行推理。
1有关未定义行为的更准确的技术定义,请参阅 this 其中提到:没有对程序行为的限制.