在同一语句中调用的执行 IO 的函数:未定义或未指定

IO-performing functions called in same statement: Undefined or unspecified?

本文关键字:函数 未定义 未指定 IO 执行 语句 调用      更新时间:2023-10-16

我开始从C++入门(第5版(中学习C++。 在完成有关表达式的章节时,我想出了一个示例程序,让我想知道:

int f1()
{
    cout << "f1n";
    return 1;
}
int f2()
{
    cout << "f2n";
    return 2;
}
int main() {
    int i = f1() + f2();
    return 0;
}

我不确定该程序是调用未定义的行为还是仅调用未指定的行为。 我知道调用函数f1f2的顺序是未指定的。 每个函数都写入标准输出作为副作用,因此充其量是未指定行的打印顺序。 在最坏的情况下,这会调用未定义的行为。

我知道答案就在C++标准中的某个地方,但对于我目前的理解水平来说,这是相当技术性的。 更温和的解释将不胜感激。

未指定的行为

程序的行为因实现而异,并且不需要符合实现来记录每个行为的效果。例如,计算顺序、相同的字符串文本是否不同、数组分配开销量等。每个未指定的行为都会导致一组有效结果之一。

评估顺序

这里没有从左到右

或从右到左C++评估的概念。这不要与运算符的从左到右和从右到左的结合性混淆:表达式 f1(( + f2(( + f3(( 由于运算符 + 的从左到右结合性,表达式 f1(( + f2((( + f3(( 被解析为 (f1(( + f2(((,但对 f3 的函数调用可以在运行时首先、最后或在 f1(( 或 f2(( 之间计算。

如果顺序很重要,你这样做

int a = f1();
int b = f2();
int i = a + b;

如果顺序不重要,那么代码就完全没问题了。

"未定义的行为"的话题在网站上相当令人讨厌。在相当长的一段时间里,有很多问题问"这是未定义的行为吗?",而回答者则争先恐后地说"是",而事实并非如此。我相信你想的是这样的情况:

对运算符操作数的值计算进行排序 在计算运算符结果的值之前。如果一面 对标量对象的影响相对于另一个对象是未排序的 对同一标量对象的副作用或使用 同一标量对象的值,并且它们不是潜在的 并发 (1.10(,行为未定义。

[ 示例:

void f(int, int);
void g(int i, int* v) {
  i = v[i++];        // the behavior is undefined
  i = 7, i++, i++;   // i becomes 9
  i = i++ + 1;       // the behavior is undefined
  i = i + 1;         // the value of i is incremented
  f(i = -1, i = -1); // the behavior is undefined
}

结束示例 ]

修改对象和调用库 I/O 函数都是副作用。除此之外,没有任何关系暗示您的示例具有未定义的行为。

我遇到的另一个问题是回答者,他们宁愿抛弃一堆听起来不错的标准语言,这些标准语言比人类友好的版本更难解析。结果,人们往往会错过更大的图景。下面是一个示例:

§1.9/15 对于每个函数调用 F,对于每个求值 A 发生在 F 和每个未发生的评估 B 中 在 F 中,但在同一线程上作为同一线程的一部分进行评估 信号处理程序(如果有(,A 在 B 之前排序,或者 BA 之前排序。

翻译:如果AB不会被测序,那么它们就会被不确定地测序。根据定义,不确定的顺序意味着它们可以以任何顺序出现,但不能重叠。

此外,在不精确的术语中,+运算符的操作数是未排序的。因此+并不代表"序列点"。因此,f1()f2()是不确定的排序,您可以推断这是未指定的行为。

DR:忘记标准,专注于学习语言。你太纠结于细节,你忽视了大局,混淆了事情。这是一篇关于这个主题的人性化论文