首页  >  文章  >  后端开发  >  “C 编程语言”中的函数链是否表现出未指定的行为?

“C 编程语言”中的函数链是否表现出未指定的行为?

Barbara Streisand
Barbara Streisand原创
2024-10-23 18:19:25367浏览

Does Function Chaining in

《C 编程语言》中的代码片段是否表现出未定义的行为?

有问题的 C 代码,由 Bjarne Stroustrup 在《C 编程语言》第四版中提供C 编程语言”使用函数链来修改字符串:

<code class="cpp">void f2() {
    std::string s = "but I have heard it works even if you don't believe in it";
    s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");

    assert(s == "I have heard it works only if you believe in it");
}</code>

此代码演示了如何链接 update() 操作来更改字符串 s。然而,据观察,该代码在不同的编译器(例如 GCC、Visual Studio 和 Clang)中表现出不同的行为。

分析

虽然代码可能看起来很简单,但它涉及未指定的顺序求值,特别是对于涉及函数调用的子表达式。虽然它不会调用未定义的行为(因为所有副作用都发生在函数调用内),但它确实表现出未指定的行为。

关键问题是子表达式的求值顺序,例如 s.find( "even") 和 s.find(" don't") 没有明确定义。这些子表达式可以在初始 s.replace(0, 4, "") 调用之前或之后进行求值,这可能会影响结果。

如果我们检查代码片段的求值顺序:

s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");

我们可以看到以下子表达式是不确定排序的(由括号中的数字表示):

  • s.replace(0, 4, "") (1)
  • s.find("偶数") (2)
  • s.replace(s.find("偶数"), 4, "仅") (3)
  • s.find("不要") (4)
  • s.replace(s.find("不要"), 6, "") (5)

每对括号内的表达式都是有序的(例如,2 在 3 之前),但它们可以按彼此不同的顺序进行计算。具体来说,不确定性存在于表达式 1 和 2 之间以及表达式 1 和 4 之间。

编译器差异

观察到的编译器行为差异可以归因于每个表达式选择的不同评估顺序编译器。在某些情况下,replace() 调用的计算方式会导致预期的行为,而在其他情况下,计算顺序会以意外的方式更改字符串。

为了说明这一点,请考虑以下情况:

  • 在某些实现中,例如 Clang,replace(0, 4, "") 在 find("even") 和 find(" don't") 之前进行评估。这确保了后续的替换调用对修改后的字符串进行操作,产生正确的结果。
  • 在其他实现中,例如 GCC 和 Visual Studio,find("even") 和 find(" don't")可以在replace(0, 4, "")之前进行评估。这可能会导致不正确的结果,因为 find 调用对原始的、未修改的字符串进行操作,可能会找到与预期不同的位置。

指定与未指定的行为

需要注意的是,此代码调用未定义的行为。未定义的行为通常涉及访问未初始化的变量或尝试访问其边界之外的内存。在这种情况下,所有副作用都发生在函数调用内,并且代码不会访问无效的内存位置。

但是,代码确实表现出未指定的行为,这意味着C 标准没有定义子表达式的求值。这可能会导致不同的编译器产生不同的结果,甚至同一程序的不同运行。

建议的更改

C 标准委员会已认识到此问题并提出更改以细化表达式求值顺序惯用语 C .对 C 20 中的 [expr.call]p5 的拟议更改指定“后缀表达式在表达式列表中的每个表达式和任何默认参数之前排序”,这将消除此代码中未指定的行为。

以上是“C 编程语言”中的函数链是否表现出未指定的行为?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn