繁体   English   中英

C和C ++与序列点和UB的差异

[英]Differences in C and C++ with sequence points and UB

我使用这篇文章未定义的行为和序列点来记录C程序中的未定义行为( UB ),并且我指出C and C++ have their own divergent rules for this [sequence points] 那么当涉及到序列点和相关的UB时, CC ++之间有什么区别? 我不能使用关于C ++序列的帖子来分析C代码中发生的事情吗?

*当然我不是在谈论不适用于CC++特性。

这个问题有两个部分,我们可以毫不费力地解决序列点规则的比较。 这并不让我们虽然太远,C和C ++是具有不同标准的不同的语言( 最新的C ++标准几乎是两倍于中大最新的C标准 ),而且即使C ++使用C作为规范性引用这将是不正确引用C的C ++标准,反之亦然,无论某些部分有多相似。 C ++标准确实引用了C标准,但这适用于小部分。

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

序列点

由于我们讨论的是序列点,所以这涵盖了C ++ 11之前和C11之前的版本。 就C99和Pre C ++ 11草案标准而言,序列点规则没有太大差别。 正如我们将在一些示例中看到的,我给出了不同的未定义行为,序列点规则不会在其中起作用。

序列点规则包含在C ++ 031.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)。
  • 以下运算符的第一个操作数的结尾:logical AND &&(6.5.13); 逻辑OR || (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); 返回语句中的表达式(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中不是。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM