什么是“序列点"?以及它们如何影响未定义的行为? [英] What are "sequence points" and how do they affect undefined behavior?

查看:57
本文介绍了什么是“序列点"?以及它们如何影响未定义的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么是序列点"?

未定义行为与序列点之间是什么关系?

What is the relation between undefined behaviour and sequence points?

我经常使用诸如 a [++ i] = i; 之类的有趣而令人费解的表达式,以使自己感觉更好.为什么我应该停止使用它们?

I often use funny and convoluted expressions like a[++i] = i;, to make myself feel better. Why should I stop using them?

如果您已阅读此书,请确保访问后续问题 未定义的行为和顺序点已重新加载.

If you've read this, be sure to visit the follow-up question Undefined behavior and sequence points reloaded.

推荐答案

C ++ 98和C ++ 03

此答案适用于C ++标准的较旧版本.该标准的C ++ 11和C ++ 14版本没有正式包含序列点".操作是先于"或未排序"或不确定地排序".最终效果基本相同,但术语不同.

C++98 and C++03

This answer is for the older versions of the C++ standard. The C++11 and C++14 versions of the standard do not formally contain 'sequence points'; operations are 'sequenced before' or 'unsequenced' or 'indeterminately sequenced' instead. The net effect is essentially the same, but the terminology is different.

免责声明:好的.这个答案有点长.因此阅读时要有耐心.如果您已经知道这些事情,那么再次阅读它们不会使您发疯.

Disclaimer : Okay. This answer is a bit long. So have patience while reading it. If you already know these things, reading them again won't make you crazy.

先决条件: C ++标准的基础知识

标准说

在执行序列中的某些指定点称为 sequence points ,先前评估的所有副作用应当完整,并且后续评估不会出现副作用.(§1.9/7)

At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. (§1.9/7)

有副作用吗?什么是副作用?

对表达式的求值会产生某些结果,并且如果执行环境的状态发生变化,则可以说表达式(其求值)会产生一些副作用.

Side effects? What are side effects?

Evaluation of an expression produces something and if in addition there is a change in the state of the execution environment it is said that the expression (its evaluation) has some side effect(s).

例如:

int x = y++; //where y is also an int

除了初始化操作外,由于 ++ 运算符的副作用, y 的值也会更改.

In addition to the initialization operation the value of y gets changed due to the side effect of ++ operator.

到目前为止,一切都很好.继续到序列点.comp.lang.c作者 Steve Summit :

So far so good. Moving on to sequence points. An alternation definition of seq-points given by the comp.lang.c author Steve Summit:

顺序点是尘埃沉降的时间点,到目前为止,已经看到的所有副作用都可以保证是完整的.

Sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete.


C ++标准中列出的常见序列点是什么?

这些是:

  • 在计算完整表达式(§1.9/16 )的末尾(完整表达式是不是另一个表达式的子表达式的表达式.) 1

  • at the end of the evaluation of full expression (§1.9/16) (A full-expression is an expression that is not a subexpression of another expression.)1

示例:

int a = 5; // ; is a sequence point here

在第一个表达式(§1.9/18 ) 2

  • a&&b(§5.14)
  • a ||b(§5.15)
  • a吗?b:c(第5.16节)
  • a,b(§5.18)(这里a,b是逗号运算符;在 func(a,a ++) 是不是逗号运算符,它只是参数 a a ++ 之间的分隔符.因此,这种情况下的行为是不确定的(如果考虑了 a 成为原始类型))
    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (here a , b is a comma operator; in func(a,a++) , is not a comma operator, it's merely a separator between the arguments a and a++. Thus the behaviour is undefined in that case (if a is considered to be a primitive type))

    发生在函数体内的任何表达式或语句(§1.9/17 )执行之前.

    at a function call (whether or not the function is inline), after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body (§1.9/17).

    1:注:对完整表达式的评估可以包括对非词汇形式的子表达式的评估完整表达的一部分.例如,与评估默认参数表达式(8.3.6)有关的子表达式被认为是在调用函数的表达式中创建的,而不是在定义默认参数的表达式中创建的

    2:所指示的运算符是内置运算符,如第5节所述.当这些运算符之一在有效上下文中被重载(第13节),从而指定了用户定义的运算符功能时,表达式指定函数调用,并且操作数形成参数列表,它们之间没有隐含的序列点.

    该标准在§1.3.12部分中将未定义行为定义为

    The Standard defines Undefined Behaviour in Section §1.3.12 as

    行为,例如由于使用错误的程序构造或错误的数据而引起的行为,对此本国际标准不加没有要求 3 .

    behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements 3.

    在这种情况下,也可能会出现未定义的行为国际标准省略了对行为的任何明确定义的描述.

    Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior.

    3:允许的不确定行为,范围从完全忽略具有不可预测结果的情况到在翻译或程序执行过程中以环境特征记录的方式表现(带有或带有-退出诊断消息的发布),终止翻译或执行(伴随诊断消息的发布).

    简而言之,未定义的行为意味着任何事情都可能发生,从守护程序从鼻子飞出到女友怀孕.

    In short, undefined behaviour means anything can happen from daemons flying out of your nose to your girlfriend getting pregnant.

    在开始讨论之前,您必须了解未定义行为之间的区别,未指定的行为和实施定义的行为.

    Before I get into that you must know the difference(s) between Undefined Behaviour, Unspecified Behaviour and Implementation Defined Behaviour.

    您还必须知道,未指定各个运算符的操作数和各个表达式的子表达式的求值顺序以及发生副作用的顺序.

    例如:

    int x = 5, y = 6;
    
    int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.
    

    另一个示例这里.

    现在§5/4 中的标准说

    • 1)在上一个序列点和下一个序列点之间,标量对象的存储值最多只能通过表达式的求值修改.

    这是什么意思?

    非正式地,它意味着两个序列点之间的变量不得被多次修改.在表达式语句中,下一个序列点通常位于终止分号,而上一个序列点位于上一个语句的末尾.表达式也可以包含中间的序列点.

    Informally it means that between two sequence points a variable must not be modified more than once. In an expression statement, the next sequence point is usually at the terminating semicolon, and the previous sequence point is at the end of the previous statement. An expression may also contain intermediate sequence points.

    从上面的句子中,以下表达式调用未定义的行为:

    From the above sentence the following expressions invoke Undefined Behaviour:

    i++ * ++i;   // UB, i is modified more than once btw two SPs
    i = ++i;     // UB, same as above
    ++i = 2;     // UB, same as above
    i = ++i + 1; // UB, same as above
    ++++++i;     // UB, parsed as (++(++(++i)))
    
    i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)
    

    但是以下表达式很好:

    i = (i, ++i, 1) + 1; // well defined (AFAIK)
    i = (++i, i++, i);   // well defined 
    int j = i;
    j = (++i, i++, j*i); // well defined
    


    • 2)此外,应仅访问先前值以确定要存储的值.
    • 是什么意思?这意味着,如果将对象写入完整表达式内,则在同一表达式内对其进行的所有访问必须直接参与要写入的值的计算.

      What does it mean? It means if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written.

      例如,在 i = i + 1 中,所有 i 的访问(在LHS和RHS中)直接参与了的计算要写入的值.很好.

      For example in i = i + 1 all the access of i (in L.H.S and in R.H.S) are directly involved in computation of the value to be written. So it is fine.

      此规则有效地限制了法律表达方式,使其可被视为在修改之前具有明显的访问权限.

      This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.

      示例1:

      std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2
      

      示例2:

      a[i] = i++ // or a[++i] = i or a[i++] = ++i etc
      

      不允许使用

      ,因为 i 的访问之一( a [i] 中的访问)与最终存储在i中的值无关(这发生在 i ++ 中),因此无论是在我们理解还是在编译器方面,都没有一种好的方法来定义访问是在存储增量值之前还是之后进行.因此,行为是不确定的.

      is disallowed because one of the accesses of i (the one in a[i]) has nothing to do with the value which ends up being stored in i (which happens over in i++), and so there's no good way to define--either for our understanding or the compiler's--whether the access should take place before or after the incremented value is stored. So the behaviour is undefined.

      示例3:

      int x = i + i++ ;// Similar to above
      


      C ++ 11的后续解答此处.


      Follow up answer for C++11 here.

      这篇关于什么是“序列点"?以及它们如何影响未定义的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆