c++ - C和C++中带有序列点和UB的差异

标签 c++ c language-lawyer undefined-behavior sequence-points

我使用了Undefined Behavior and Sequence Points这个帖子来记录C语言程序中的undefined behavior(UB),并指出C and C++ have their own divergent rules for this [sequence points]。那么,对于顺序点和相关的UB,C和C++之间有什么区别?我不能使用有关C++序列的帖子来分析C代码中发生的情况吗?

*当然,我并不是在说C++的功能不适用于C

最佳答案

这个问题有两个部分,我们可以比较麻烦地处理序列点规则。但是,这并不能使我们太过分,C和C++是具有不同标准的不同语言(latest C++ standard几乎是latest C standard的两倍),即使C++使用C作为规范性引用,引用C++也是不正确的C的标准,反之亦然,无论某些部分有多相似。 C++标准确实明确引用了C标准,但这是针对小部分的。

第二部分是C和C++之间的undefined behavior的比较,可能会有一些大的差异,并且可能无法枚举未定义行为的所有差异,但是我们可以给出一些指示性示例。

顺序点

由于我们在谈论sequence points,因此涵盖了C++ 11和C11之前的版本。据我所知,在C99和Pre C++ 11草案标准之间,顺序点规则没有太大差异。正如我们将在某些示例中看到的,我给出了不同的未定义行为,序列点规则在其中不起作用。

序列点规则在closest draft C++ standard to C++031.9程序执行中介绍,该程序说:

  • 每个完整表达式的评估完成时都有一个序列点。
  • 调用函数(函数是否为内联)时,在对所有函数求值后都会有一个序列点
    函数参数(如果有)在函数体内执行任何表达式或语句之前发生。
  • 复制返回值之后以及在执行任何外部表达式之前还有一个序列点
    功能13)。即使没有相应的函数调用,C++中的多个上下文也会导致函数调用的评估
    语法出现在翻译单元中。 [示例:对新表达式的求值调用一个或多个分配,
    构造函数;参见5.3.4。再举一个例子,在上下文中可能会调用转换函数(12.3.2)
    其中没有函数调用语法。 — —结束示例]序列指向函数入口和函数导出
    (如上所述)是所求值函数调用的功能,无论调用
    功能可能是。
  • 在每个表达式的求值中
    a && b
    a || b
    a ? b : c
    a , b
    

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

  • 我将使用C99标准Annex C草案中的序列点列表,尽管它不是规范性的,但我发现它所引用的规范性部分没有不同之处。它说:

    The following are the sequence points described in 5.1.2.3:


  • 对参数进行求值后(6.5.2.2),对函数的调用。
  • 以下运算符的第一个操作数的结尾:逻辑AND &&(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)。
  • 与每个格式化的输入/输出函数转换关联的 Action 之后
    说明符(7.19.6,7.24.2)。
  • 在每次调用比较函数之前和之后立即,以及
    在对比较函数的任何调用与对象的任何移动之间
    作为参数传递给该调用(7.20.5)。

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

    未定义的行为

    对于序列点和未定义行为的典型示例,例如5表达式一节中涉及的那些涉及在序列点内多次修改变量的示例,我无法提出一个在一个示例中未定义但在一个示例中未定义的示例。其他。在C99中说:

    Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.72) Furthermore, the prior value shall be read only to determine the value to be stored.73)



    并提供以下示例:
    i = ++i + 1;
    a[i++] = i;
    

    在C++中说:

    Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.57) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined



    并提供以下示例:
    i = v[i ++]; / / the behavior is undefined
    i = ++ i + 1; / / the behavior is undefined
    

    在C++ 11和C11中,我们确实有一个主要区别,它在Assignment operator sequencing in C11 expressions中涉及如下:
    i = ++i + 1;
    

    这是由于即使顺序规则相同,预递增也是C++ 11中的左值,而不是C11中的左值。

    在与顺序点无关的区域中,我们确实存在重大差异:
  • 在C语言中,不确定的值的使用一直是未定义的,而在C++语言中,直到最近的C++ 1y标准草案才明确定义了它。这是我对Has C++ standard changed with respect to the use of indeterminate values and undefined behavior in C++1y?
  • 的回答
  • 通过联合修剪的类型一直在C中得到很好的定义,但在C++中却没有得到定义,至少不管它是否是未定义的行为,它都是可争议的。我对Why does optimisation kill this function?
  • 的回答中对此有一些引用
  • 在C++中,仅返回值结尾的函数是未定义的行为,而在C中,如果使用值,则仅是未定义的行为。

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

    关于c++ - C和C++中带有序列点和UB的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24896653/

    相关文章:

    c++ - 范围枚举 : error: cannot convert ‘int’ to ‘Handle’ in initialization

    c++ - 这两个说法在N4140中不是不兼容吗?

    c++ - 切换到 clang 3.4 和 libc++ 时找不到标准 header

    c++ - 在派生类中重写运算符 new/delete

    c - 如果用指针引用常量字符串,是否永久占用内存?

    c - int main(void) 中的 void 不是多余的吗?

    c++ - 如何解释 c++ 标准中的规则 [namespace.udir]p2?

    c++ - 从双指针数组读取 - NULL

    c++ - 在 Qt 中创建原始 GL 上下文?

    c - C 程序如何将空格参数传递给 libc system(3) 调用?