4

我使用这篇文章Undefined Behavior and Sequence Points来记录C程序中的未定义行为(UB),它指向我。那么在序列点和相关的UB方面, CC++之间有什么区别?我不能用一篇关于C++序列的帖子来分析C代码中发生的事情吗?C and C++ have their own divergent rules for this [sequence points]

* 当然我不是在谈论C++不适用于C.

4

1 回答 1

4

这个问题有两个部分,我们可以毫不费力地解决序列点规则的比较。这并没有让我们走得太远,C 和 C++ 是不同的语言,具有不同的标准(最新的 C++ 标准几乎是最新C 标准的两倍),即使 C++ 使用 C 作为规范性参考,它也是不正确的引用 C 的 C++ 标准,反之亦然,无论某些部分有多么相似。C++ 标准确实明确引用了 C 标准,但这是针对小部分的。

第二部分是C 和 C++ 之间未定义行为的比较,可能存在一些很大的差异,列举未定义行为的所有差异可能是不可能的,但我们可以给出一些指示性示例。

序列点

由于我们谈论的是序列点,所以这涵盖了 pre C++11 和 pre C11。据我所知,C99 和 Pre C++11 草案标准之间的序列点规则差别不大。正如我们将在我给出的不同未定义行为的一些示例中看到的那样,序列点规则在其中不起作用。

序列点规则包含在最接近 C++03部分1.9 程序执行的 C++ 标准草案中,其中说:

  • 每个完整表达式12)的评估完成时都有一个序列点。
  • 调用函数时(无论该函数是否内联),在对所有函数参数(如果有)求值之后都有一个序列点,该序列点发生在函数体中的任何表达式或语句执行之前。
  • 在复制返回值之后和执行函数13)之外的任何表达式之前,还有一个序列点。C++ 中的几个上下文会导致对函数调用进行评估,即使翻译单元中没有出现相应的函数调用语法。[ 示例:新表达式的求值调用一个或多个分配和构造函数;见 5.3.4。对于另一个示例,转换函数 (12.3.2) 的调用可能出现在没有出现函数调用语法的上下文中。—end example ] 函数入口和函数出口处的序列点(如上所述)是评估后的函数调用的特征,无论调用函数的表达式的语法可能是什么。
  • 在每个表达式的评估中

    a && b
    a || b
    a ? b : c
    a , b
    

    使用这些表达式(5.14、5.15、5.16、5.18)中运算符的内置含义,在第一个表达式求值之后有一个序列点 14)。

我将使用 C99 标准草案中的序列点列表Annex C,尽管它不是规范的,但我无法发现它与它引用的规范部分存在分歧。它说:

以下是5.1.2.3中描述的顺序点:

  • 在评估参数之后对函数的调用 (6.5.2.2)。
  • 以下运算符的第一个操作数的结尾:逻辑与 && (6.5.13); 逻辑或 || (6.5.14);有条件的?(6.5.15);逗号 , (6.5.17)。
  • 完整声明符的结尾:声明符(6.7.5);
  • 完整表达式的结尾:初始化器(6.7.8);表达式语句中的表达式 (6.8.3);选择语句(if 或 switch)的控制表达式(6.8.4);while 或 do 语句的控制表达式 (6.8.5);for 语句 (6.8.5.3) 的每个表达式;return 语句中的表达式 (6.8.6.4)。

以下条目似乎在 C++ 标准草案中没有等效项,但这些条目来自 C++ 通过引用合并的 C 标准库:

  • 紧接在库函数返回之前 (7.1.4)。
  • 在与每个格式化输入/输出函数转换说明符(7.19.6、7.24.2)关联的操作之后。
  • 在每次调用比较函数之前和之后,以及在对比较函数的任何调用与作为参数传递给该调用的对象的任何移动之间(7.20.5)。

因此,这里的 C 和 C++ 之间没有太大区别。

未定义的行为

当涉及到序列点和未定义行为的典型示例时,例如在处理在序列点内多次修改变量的部分5 表达式中所涵盖的那些,我无法想出一个在一个中未定义但在另一个中未定义的示例. 在 C99 中它说:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。72)此外,应仅读取先验值以确定要存储的值。73)

它提供了以下示例:

i = ++i + 1;
a[i++] = i;

在 C++ 中它说:

除非另有说明,单个运算符的操作数和单个表达式的子表达式的求值顺序以及副作用发生的顺序是未指定的。57) 在前一个和下一个序列点之间,标量对象应修改其存储值最多一次通过表达式的评估。此外,只能访问先验值以确定要存储的值。对于完整表达式的子表达式的每个允许排序,都应满足本段的要求;否则行为未定义

并提供以下示例:

i = v[i ++]; / / the behavior is undefined
i = ++ i + 1; / / the behavior is undefined

在 C++11 和 C11 中,我们确实有一个主要区别,这在 C11 表达式中的赋值运算符排序中有所介绍,如下所示:

i = ++i + 1;

这是由于预增量的结果在 C++11 中是左值,但在 C11 中不是,即使排序规则相同。

我们在与序列点无关的领域确实存在重大差异:

可能还有更多的例子,但这些是我之前写过的。

于 2014-07-23T02:19:35.400 回答