int i = f1() * f2() 的未定义行为
Posted
技术标签:
【中文标题】int i = f1() * f2() 的未定义行为【英文标题】:undefined behavior for int i = f1() * f2() 【发布时间】:2021-08-10 23:33:09 【问题描述】:我很困惑为什么这会导致未定义的行为。让我先复制粘贴教科书上的解释,然后展示我自己完美运行的代码和程序。
Precedence 指定操作数的分组方式。它什么也没说 关于计算操作数的顺序。在大多数情况下, 该顺序在很大程度上未指定。在以下表达式中*
int i = f1() * f2();
: *我们知道必须先调用f1
和f2
才能进行乘法运算。毕竟是他们的 结果相乘。然而,我们无从得知 f1 是否会在 f2 之前被调用,反之亦然。对于运营商 不指定评估顺序,表达式会出错 引用和更改同一个对象。这样做的表达式有 未定义的行为(第 2.1.2 节,第 36 页)。举个简单的例子,-- C++ 入门 - Stanley B. Lippman 第 193 页
所以,我尝试通过编写自己的代码来应用它,但我从来没有得到未定义的行为?谁能解释一下这是什么意思?
#include <iostream>
using std::cout;
using std::endl;
int f1() return (5 + 5 * 4 / 2 - 3); // 12
int f2() return (10 + 2 * 10 / 2 - 5); // 15
int main()
int i = f1() * f2();
cout << i << endl;
return 0;
【问题讨论】:
您的两个函数都不会修改同一个变量,因此它们的求值顺序无关紧要。it is an error for an expression to refer to and change the same object
好吧,f1
和 f2
都不会改变同一个对象。
第一:您希望如何检测未定义的行为?也就是说,如果您遇到未定义的行为,您希望看到的输出或程序行为会向您表明这一点吗?第二:为什么您认为您的示例代码会调用未定义的行为? f1
和 f2
都不会修改 i
或其他任何与此相关的内容。
您可以通过实验验证 UB 的存在,但永远不能absence UB。 (发布的代码是可以的,但你不能仅仅通过运行它来证明)。
顺便说一句,李普曼在这里大错特错。 f1() 和 f2() 访问和修改同一个变量不是未定义的行为。未指定顺序,但任一顺序均已明确定义。您可以在 f2() 之后调用 f1() 或在 f1() 之后调用 f2(),但没有别的。
【参考方案1】:
你搞错了。作者的意思是如果订单很重要,则未指定。在您的情况下,评估顺序无关紧要。其实这个函数也可以是constexpr
。但如果你有这样的事情:
int i = 0;
int f1() return (i++) * 3;
int f2() return (i++) * 4;
int main()
int a = f1() + f2();
现在,如果先调用f1
,则结果为4。如果先调用f2
,则结果为3。因此,未指定。
我从来没有遇到过未定义的行为?
你不能仅仅通过运行程序就知道这一点。
【讨论】:
i
没有初始化的情况下,结果4和3分别如何?
@ProgrammingRage 静态变量默认初始化为零。在这个例子中,i
是一个(全局)静态变量,因此它的值一开始就为零。
好的,所以我在这里试用了您的代码:imgur.com/gh2ke8C 显然 f1()
返回了 0,f2()
返回了 4,不知何故 a
是 18????
@ProgrammingRage 因为您多次调用了这些函数。使用您的调试器逐步查看发生了什么。见What is a debugger and how can it help me diagnose problems?
@ProgrammingRage 欢迎来到具有副作用的函数空间(也就是非纯函数)【参考方案2】:
你的代码没问题。
表达式引用和更改同一个对象是错误的
(我的粗体字)
您不会更改表达式中的任何对象,因此该规则不适用。
以下是该规则何时适用的示例:
int a = 42;
int i = a++ * a++;
请注意,如果更改发生在函数中,它将不适用:
int a = 42;
int foo() return a++;
int i = foo() * foo();
这是因为 UB 仅在 two accesses to an object are unsequenced relative to each other 时发生,即可以以任何顺序发生,包括并行发生。这不一定意味着“并行线程”,也可能意味着“单线程,但执行任务的处理器指令可能是交错的”。
但是同一线程上的两个函数调用不能并行发生(并且它们的指令不能交错)。相反,在这种情况下,它们是不确定排序的,即一个紧接着一个,但不确定哪个是第一个。
还要注意
从 C++17 开始不再适用。
【讨论】:
i = (a=2) * (a=3)
也会是这样的例子吗? (因为,如果是的话,那我觉得比i = i++ * i++
更简单的例子@
抱歉,我多次阅读您的回答,但当您说indeterminately sequenced
但不能交错时,我不明白。一方面你是说顺序是不确定的,可以按任何顺序发生,但不能混合???
@ProgrammingRage he(?) 基本上表示函数是同步调用的。 (即控件在第一个函数调用返回之前不会转到另一个函数调用)。
@ProgrammingRage 这不是错误,而是功能!
@ProgrammingRage ***.com/a/2934909/10147399【参考方案3】:
来自 C++ 标准草案:
3.64 [defns.undefined] 未定义的行为
本文档没有要求的行为 [注 1:未定义的行为可能是 当本文档省略任何明确的行为定义时预期 或者当程序使用错误的构造或错误的数据时。 允许的未定义行为范围从忽略情况 完全具有不可预测的结果,在翻译过程中表现得很好 或以文件化方式执行程序的特征 环境(无论是否发布诊断消息),以 终止翻译或执行(发出 诊断信息)。许多错误的程序结构不会产生 未定义的行为;他们需要被诊断出来。评价一个 常量表达式 ([expr.const]) 从不显式地表现出行为 在 [intro] 到 [cpp] 中指定为未定义。 ——尾注]
因此,几乎所有事情都是可能的,甚至是给定实现的可预测行为。
但是,您的程序没有显示任何 UB。 f1 和 f2 没有任何边界效应,因此它们的评估顺序没有影响。
【讨论】:
是的,我知道f1()
和f2()
没有任何UB。但我试图了解什么代码会导致 UB。以上是关于int i = f1() * f2() 的未定义行为的主要内容,如果未能解决你的问题,请参考以下文章