【问题标题】:Sequence point after a return statement?return 语句后的序列点?
【发布时间】:2013-03-16 06:49:26
【问题描述】:

在我对here 问题的回答中,我解释了在return 语句的同一行上的全局变量上使用后缀++ 时发生的情况。

C11 的信息性附录 C 指出在 return 之后有一个序列点,并参考规范性章节 6.8.6.4,其中没有关于序列点的文本。

我在 C 标准的什么地方可以找到规范文本,说明在 return 语句之后有一个序列点?

(我只在 7.1.4/3 找到了说明库函数的规范文本,作为一种特殊情况。)

【问题讨论】:

    标签: c return language-lawyer sequence-points


    【解决方案1】:

    C 2011 (draft n1570) 6.8 4:“以下每一项都是完整的表达式:……return 语句中的(可选)表达式。在完整表达式的求值和要求值的下一个完整表达式的求值之间存在一个序列点。”

    所以从技术上讲,序列点不在 return 之后,而是在 return 中的表达式评估和下一个表达式之间。考虑这段代码,当a 最初为 0 时调用:

    int a = 0;
    
    int Foo(void) { return a++; }
    
    void Bar(void)
    {
        int b = Foo() + a;
        …
    }
    

    Foo() + a 中,未指定首先评估Foo() 还是a。我们将根据两个潜在规则(return 之后的序列点与 return 表达式和下一个完整表达式之间的序列点)来考虑这两个顺序。如果实现首先执行a,那么它必须执行:

    a
    Sequence point
    Foo()
    +
    

    然后会出现其他一些完整的表达式,因此,根据任一规则,都会有一个序列点,就我们而言,无论哪种方式,这段代码都是相同的。结果是b被设置为0。

    如果实现首先执行Foo(),那么,使用“return之后的序列点”规则,实现必须这样做:

    Sequence point
    Foo()
    Sequence point
    a
    +
    

    此代码将定义行为:aFoo 中的副作用递增,并且在访问a 之前完成,然后执行+。结果是a被设置为1。虽然使用“return之后的顺序点”规则,结果可能是0或1,但只是未指定使用两个顺序中的哪一个;行为并非完全未定义。

    但是,如果实现首先执行 Foo() 并使用标准 C 规则“return 表达式和下一个完整表达式之间的序列点”,那么我们有:

    Sequence point
    Foo()
    ???
    a
    ???
    +
    ???
    

    “???”标记可能需要的序列点的位置——return 之后和下一个完整表达式之前的任何位置。在这种情况下,a 的值可能会在a 中访问并在Foo() 中修改,并且没有中间序列点。那是未定义的行为。

    因此,“return表达式之后的序列点,下一个完整表达式之前的序列点”规则与“return之后的序列点”不同;在此示例中,第一个具有未定义的行为,而第二个则没有。

    【讨论】:

    • 谢谢,6.8/4 中的文字正是我要找的。显然这在 C11 中得到了澄清,因为 C99 没有§4。不过,关于未定义的行为,我不遵循您的示例,因为在评估所有函数参数之后,但在调用函数之前,还有一个序列点。因此,每个带有 return 语句的函数在开头和结尾都受到序列点的“保护”。所以我相信你的例子只是 unspecified 行为,因为我们不知道是先评估 Foo() 还是 a
    • @Lundin:关于参数评估和函数调用之间的序列点的好点。这意味着这两个规则是有区别的。我已经更新了答案。
    • 是否存在以下情况:在返回值的评估和对封闭表达式的任何部分的评估之间缺少序列点将被评估文本允许编译器比其他情况更有用地处理代码有可能吗?如果没有,是否有任何理由寻求制作或使用质量编译器的人需要关心是否有一个序列点是强制性的?
    【解决方案2】:

    我不认为你会找到你要找的东西。 no text regarding sequence points can be found 是的,这仅在第 6.8 p4 节中有所暗示。

    第 1.9 节(脚注 11)中的 C++ 标准 (ISO/IEC 14882:2003) 声明了返回后的序列点没有在 C 标准中的任何位置明确写入的事实:

    11) 函数返回的序列点在 ISO C 中没有明确指定,可以认为与序列冗余 指向完整的表达式,但额外的清晰度在 C++ 中很重要。在 C++ 中,有更多的方法可以终止被调用函数 它的执行,例如抛出异常。

    【讨论】:

    • @larsmans 是的......这有点多余,不是吗?感谢您的修复。
    • @Mike 谢谢。根据 6.8 §4,这似乎已在 C11 中得到纠正。 C99 没有那个段落。
    猜你喜欢
    • 1970-01-01
    • 2016-06-07
    • 2022-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    • 2017-02-05
    • 1970-01-01
    相关资源
    最近更新 更多