什么是序列点,它们与未定义的行为有何关系?

发布于 2024-10-02 12:26:01 字数 870 浏览 5 评论 0 原文

什么是“序列点”?

未定义行为和序列点之间有什么关系?

我经常使用诸如a[++i] = i;之类有趣且令人费解的表达方式,让自己感觉好一点。我为什么要停止使用它们?

如果您已阅读本文,请务必访问后续问题未定义的行为和重新加载的序列点


(Note: This is meant to be an entry to Stack Overflow's C++ FAQ. If you want to critique the idea of providing an FAQ in this form, then the posting on meta that started all this would be the place to do that. Answers to that question are monitored in the C++ chatroom, where the FAQ idea started out in the first place, so your answer is very likely to get read by those who came up with the idea.)

What are "sequence points"?

What is the relation between undefined behaviour and sequence points?

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.



(Note: This is meant to be an entry to Stack Overflow's C++ FAQ. If you want to critique the idea of providing an FAQ in this form, then the posting on meta that started all this would be the place to do that. Answers to that question are monitored in the C++ chatroom, where the FAQ idea started out in the first place, so your answer is very likely to get read by those who came up with the idea.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

囍笑 2024-10-09 12:26:01

C++98 和 C++03

此答案适用于旧版本的 C++ 标准。该标准的 C++11 和 C++14 版本并未正式包含“序列点”;操作被“先排序”或“未排序”或“不确定排序”代替。净效应本质上是相同的,但术语不同。


免责声明:TL'DR。

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


什么是序列点?

标准说

在执行序列中称为序列点的某些指定点,之前评估的所有副作用
应是完整的,并且后续评估不会产生副作用。 (§1.9/7)

副作用?什么是副作用?

表达式的求值会产生一些结果,如果此外执行环境的状态发生变化,则可以说该表达式(其求值)具有一些副作用。

例如:

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

除了初始化操作之外,由于 ++ 运算符的副作用,y 的值也会发生变化。

到目前为止,一切都很好。继续讨论序列点。 comp.lang.c 作者 Steve Summit 给出的 seq-points 交替定义:

序列点是一个时间点,尘埃落定,迄今为止所看到的所有副作用都保证完成。


C++ 标准中列出的常见序列点有哪些?

它们是:

  • 在完整表达式求值结束时 (§1.9/16)(完整表达式是不是另一个表达式的子表达式的表达式。)1

    示例:

    int a = 5; // ;这里是一个序列点
    
  • 在第一个表达式求值之后对以下每个表达式求值 (§1.9/18) 2

    • a && b (§5.14)
    • <代码>一个|| b (§5.15)
    • <代码>一个? b : c (§5.16)
    • a , b (§5.18) (这里 a , b 是逗号运算符;在 func(a,a++) , 中是不是逗号运算符,它只是参数 aa++ 之间的分隔符,因此在这种情况下行为未定义(如果考虑 a)。成为原始类型))
  • 在函数调用时(无论函数是否内联),在评估所有函数参数(如果有)之后
    在执行函数体中的任何表达式或语句之前发生 (§1.9/17)。

1 :注意:完整表达式的计算可以包括非词法子表达式的计算
完整表达的一部分。例如,计算默认参数表达式(8.3.6)所涉及的子表达式被认为是在调用函数的表达式中创建的,而不是在定义默认参数的表达式中创建的

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


什么是未定义行为?

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

行为,例如由于使用错误的程序构造或错误数据而可能出现的行为,本国际标准对此没有要求3

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

3:允许的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式进行行为(带有或带有-
发出诊断消息),终止翻译或执行(发出诊断消息)。


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

在我开始讨论之前,您必须了解 未定义行为、未指定行为和实现定义的行为

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

例如:

int x = 5, y = 6;

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

另一个例子 这里


现在§5/4中的标准说

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

这是什么意思?

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

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

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

  • 此外,仅应访问先前值以确定要存储的值。

这是什么意思?这意味着如果在完整表达式中写入对象,则同一表达式中对其的任何和所有访问都必须直接参与要写入的值的计算

例如,在 i = i + 1 中,i 的所有访问(左轴和右轴)都直接参与计算被写下来。所以没关系。

该规则有效地将法律表达式限制为访问明显先于修改的表达式。

示例 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++ 中),因此没有好的方法来定义(无论是我们的理解还是编译器的理解)访问应该在存储递增值之前还是之后进行。所以行为是未定义的。

示例 3:

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

C++11 的后续答案此处< /a>.

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 : TL'DR.

Pre-requisites : An elementary knowledge of C++ Standard


What are Sequence Points?

The Standard says

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).

For example:

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

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

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.


What are the common sequence points listed in the C++ Standard?

Those are:

  • 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

    Example :

    int a = 5; // ; is a sequence point here
    
  • in the evaluation of each of the following expressions after the evaluation of the first expression (§1.9/18) 2

    • 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))
  • 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 : Note : the evaluation of a full-expression can include the evaluation of subexpressions that are not lexically
part of the full-expression. For example, subexpressions involved in evaluating default argument expressions (8.3.6) are considered to be created in the expression that calls the function, not the expression that defines the default argument

2 : The operators indicated are the built-in operators, as described in clause 5. When one of these operators is overloaded (clause 13) in a valid context, thus designating a user-defined operator function, the expression designates a function invocation and the operands form an argument list, without an implied sequence point between them.


What is Undefined Behaviour?

The Standard defines Undefined Behaviour in Section §1.3.12 as

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 : permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or with-
out the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).


What is the relation between Undefined Behaviour and Sequence Points?

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

You must also know that 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.

For example:

int x = 5, y = 6;

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

Another example here.


Now the Standard in §5/4 says

    1. 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.

What does it mean?

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)

But the following expressions are fine:

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

    1. Furthermore, the prior value shall be accessed only to determine the value to be stored.

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.

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.

Example 1:

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

Example 2:

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

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.

Example 3 :

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

Follow up answer for C++11 here.

桜花祭 2024-10-09 12:26:01

这是我之前的回答的后续内容。并包含 C++11 相关材料。


先决条件:关系(数学)的基本知识。


C++11 中真的没有序列点吗?

是的!这是真的。

序列点已替换为之前排序之后排序(以及未排序不确定排序) strong>) C++11 中的关系


这个“之前排序”到底是什么?

顺序之前(§1.9/13)是一个关系,它是:

由单个线程执行的评估之间,并产生严格的偏序1

形式上,这意味着给定任意两个评估(见下文) AB ,如果 A 先于 B 执行,则 A 的执行应先于 B 的执行。如果 A 未在 B 之前排序,且 B 未在 A 之前排序,则 AB无序 2

AB 之前排序时,评估 AB不确定排序的 B 排在 A 之前,但未指定是哪个3

<子>[注释]
<子>
1 : 严格偏序是一个 二元关系 " <" 超过一个不对称<的集合P /code>transitive,即P 中的所有 abc,我们有:
<子>........(i)。如果 a < b 然后 ← (b < a)(不对称);
........(二)。如果 a < b 且 b < c 那么 a < c(传递性)。
2:未排序的评估的执行可以重叠
3:不确定顺序的评估不能重叠,但可以先执行其中一个。


C++11 上下文中“评估”一词的含义是什么?

在 C++11 中,表达式(或子表达式)的计算通常包括:

现在(§1.9/14)说:

与完整表达式相关的每个值计算和副作用都会在与要评估的下一个完整表达式相关的每个值计算和副作用之前排序。< /p>

  • 简单的例子:

    int x;
    x = 10;
    ++x;

    ++x 相关的值计算和副作用在 x = 10; 的值计算和副作用之后排序;


因此Undefined Behaviour 和上述事情之间一定有某种关系吧?

是的!对。

在(§1.9/15)中提到

除非另有说明,否则各个运算符的操作数和各个表达式的子表达式的求值都是无序的4

例如:

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. + 运算符的操作数的计算相对于彼此是无序的。
  2. <<>> 运算符的操作数的求值相对于彼此是无序的。

4:在执行过程中多次求值的表达式中
对于程序来说,其子表达式的未排序不确定排序计算不需要在不同的计算中一致地执行。

(§1.9/15)
操作数的值计算
运算符在运算符结果的值计算之前进行排序。

这意味着在 x + y 中,xy 的值计算在 (x + y)< 的值计算之前排序。 /代码>。

更重要的是

(§1.9/15) 如果标量对象上的副作用相对于任一者都是无序的

(a) 同一标量对象的另一个副作用

(b) 使用同一标量对象的值进行值计算。

该行为未定义

示例:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // 未定义行为
  2. i = ++i + i++; // 未定义行为
  3. i = ++i + ++i; // 未定义行为
  4. i = v[i++]; // 未定义的行为
  5. i = v[++i]: // 明确定义的行为
  6. i = i++ + 1; // 未定义行为
  7. i = ++i + 1; // 明确定义的行为
  8. ++++i; // 明确定义的行为
  9. f(i = -1, i = -1); // 未定义的行为(见下文)

当调用函数时(无论函数是否内联),与任何参数表达式或指定被调用函数的后缀表达式相关的每个值计算和副作用,都会在执行函数中的每个表达式或语句之前进行排序。被调用函数的主体。 [注意: 与不同参数表达式相关的值计算和副作用是不排序的。 - 尾注]

表达式(5)(7)(8)不会调用未定义的行为。查看以下答案以获得更详细的解释。


最终说明< /strong> :

如果您发现帖子中有任何缺陷,请发表评论。高级用户(代表数>20000)请随时编辑帖子以纠正拼写错误和其他错误。

This is a follow up to my previous answer and contains C++11 related material..


Pre-requisites : An elementary knowledge of Relations (Mathematics).


Is it true that there are no Sequence Points in C++11?

Yes! This is very true.

Sequence Points have been replaced by Sequenced Before and Sequenced After (and Unsequenced and Indeterminately Sequenced) relations in C++11.


What exactly is this 'Sequenced before' thing?

Sequenced Before(§1.9/13) is a relation which is:

between evaluations executed by a single thread and induces a strict partial order1

Formally it means given any two evaluations(See below) A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced 2.

Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which3.

[NOTES]

1 : A strict partial order is a binary relation "<" over a set P which is asymmetric, and transitive, i.e., for all a, b, and c in P, we have that:

........(i). if a < b then ¬ (b < a) (asymmetry);
........(ii). if a < b and b < c then a < c (transitivity).
2 : The execution of unsequenced evaluations can overlap.
3 : Indeterminately sequenced evaluations cannot overlap, but either could be executed first.


What is the meaning of the word 'evaluation' in context of C++11?

In C++11, evaluation of an expression (or a sub-expression) in general includes:

  • value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and

  • initiation of side effects.

Now (§1.9/14) says:

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

  • Trivial example:

    int x;
    x = 10;
    ++x;

    Value computation and side effect associated with ++x is sequenced after the value computation and side effect of x = 10;


So there must be some relation between Undefined Behaviour and the above-mentioned things, right?

Yes! Right.

In (§1.9/15) it has been mentioned that

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced4.

For example :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. Evaluation of operands of + operator are unsequenced relative to each other.
  2. Evaluation of operands of << and >> operators are unsequenced relative to each other.

4: In an expression that is evaluated more than once during the execution
of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations.

(§1.9/15)
The value computations of the operands of an
operator are sequenced before the value computation of the result of the operator.

That means in x + y the value computation of x and y are sequenced before the value computation of (x + y).

More importantly

(§1.9/15) If a side effect on a scalar object is unsequenced relative to either

(a) another side effect on the same scalar object

or

(b) a value computation using the value of the same scalar object.

the behaviour is undefined.

Examples:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note]

Expressions (5), (7) and (8) do not invoke undefined behaviour. Check out the following answers for a more detailed explanation.


Final Note :

If you find any flaw in the post please leave a comment. Power-users (With rep >20000) please do not hesitate to edit the post for correcting typos and other mistakes.

此刻的回忆 2024-10-09 12:26:01

C++17 (N4659) 包含提案 细化惯用 C++ 的表达式求值顺序
它定义了更严格的表达式求值顺序。

特别是下面的句子

8.18 赋值和复合赋值运算符
....

在所有情况下,赋值都在值之后排序
右操作数和左操作数的计算,以及赋值表达式的值计算之前。
右操作数在左操作数之前排序。

以及以下说明

表达式X被认为是在表达式Y之前排序,如果每个
值计算和与表达式 X 相关的每个副作用都在每个值之前排序
计算以及与表达式Y相关的每个副作用。

使先前未定义行为的几种情况有效,包括有问题的情况:

a[++i] = i;

然而,其他几种类似的情况仍然会导致未定义的行为。

N4140 中:

i = i++ + 1; // the behavior is undefined

但在 N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

当然,使用符合 C++17 的编译器并不一定意味着应该开始编写这样的表达式。

C++17 (N4659) includes a proposal Refining Expression Evaluation Order for Idiomatic C++
which defines a stricter order of expression evaluation.

In particular, the following sentence

8.18 Assignment and compound assignment operators:
....

In all cases, the assignment is sequenced after the value
computation of the right and left operands, and before the value computation of the assignment expression.
The right operand is sequenced before the left operand.

together with the following clarification

An expression X is said to be sequenced before an expression Y if every
value computation and every side effect associated with the expression X is sequenced before every value
computation and every side effect associated with the expression Y.

make several cases of previously undefined behavior valid, including the one in question:

a[++i] = i;

However several other similar cases still lead to undefined behavior.

In N4140:

i = i++ + 1; // the behavior is undefined

But in N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

Of course, using a C++17 compliant compiler does not necessarily mean that one should start writing such expressions.

叹沉浮 2024-10-09 12:26:01

我猜这种变化有一个根本原因,它不仅仅是为了使旧的解释更清晰而做的:这个原因是并发性。未指定的阐述顺序只是从几种可能的串行顺序中选择一种,这与之前和之后的排序有很大不同,因为如果没有指定的顺序,则可以并发评估:而旧规则则不然。例如:

f (a,b)

先是 a 然后 b,或者 b 然后 a。现在,a 和 b 可以使用交错的指令甚至在不同的内核上进行评估。

I am guessing there is a fundamental reason for the change, it isn't merely cosmetic to make the old interpretation clearer: that reason is concurrency. Unspecified order of elaboration is merely selection of one of several possible serial orderings, this is quite different to before and after orderings, because if there is no specified ordering, concurrent evaluation is possible: not so with the old rules. For example in:

f (a,b)

previously either a then b, or, b then a. Now, a and b can be evaluated with instructions interleaved or even on different cores.

黑寡妇 2024-10-09 12:26:01

C99(ISO/IEC 9899:TC3) 中,到目前为止,该讨论似乎没有出现,以下是关于评估顺序的 steteents。

[...]子表达式的求值顺序和
发生的副作用均未明确。 (第 6.5 节,第 67 页)

操作数的求值顺序未指定。如果尝试
用于修改赋值运算符的结果或访问它
在下一个序列点之后,行为[原文如此]未定义。(节
6.5.16 第 91 页)

In C99(ISO/IEC 9899:TC3) which seems absent from this discussion thus far the following steteents are made regarding order of evaluaiton.

[...]the order of evaluation of subexpressions and the order in which
side effects take place are both unspecified. (Section 6.5 pp 67)

The order of evaluation of the operands is unspecified. If an attempt
is made to modify the result of an assignment operator or to access it
after the next sequence point, the behavior[sic] is undefined.(Section
6.5.16 pp 91)

酸甜透明夹心 2024-10-09 12:26:01

该标准规定,只有当优化转换不会显着影响任何定义的程序的行为时,才可以执行优化转换。序列点规则被编写为允许以不交叉序列点的方式对动作进行重新排序,即使这种重新排序的效果可能是可观察到的,通过将任何可能导致某个动作的效果的动作分类为未定义行为。观察到允许的转变。

这种规则制定方法的一个不幸的后果是,它使得程序有必要明确强制执行操作的顺序,即使在不重要的情况下也是如此。例如,Java可以在不使用任何内存屏障的情况下缓存字符串的哈希码;缺乏内存屏障可能会导致线程感知到哈希码没有被缓存,即使在另一个线程实际缓存了它之后,从而执行冗余哈希值计算,但偶尔的额外计算的成本通常会很大低于每次访问时添加内存屏障的成本。然而,在 C 中,当另一个线程正在修改缓存的哈希码字段时尝试读取它会产生未定义行为,即使在读取尝试的唯一可能影响是生成旧值(表明哈希码不是“”的平台上)也是如此。 t 缓存)或最后写入的值(始终是正确的哈希码)。

The Standard specifies that optimizing transforms may be performed if any only if they do not observably affect the behavior of any defined program. The sequence-point rules are written to allow reordering of actions in ways that don't cross sequence points, even if the effects of such reordering might be observable, by classifying as Undefined Behavior any actions that would make it possible for the effects of an allowable transformation to be observed.

An unfortunate consequence of this approach to rule making is that it makes it necessary for programs to explicitly force the sequencing of actions even in cases where it wouldn't matter. For example, Java can cache strings' hash codes without using any memory barriers; the lack of memory barriers may cause a thread to perceive that the hash code isn't cached, even after another thread has actually cached it, and thus perform a redundant hash value computation, but the cost of the occasional extra calculations will generally be significantly below the cost of adding a memory barrier on every access. In C, however, attempting to read the cached hash code field while another thread is modifying it would yield Undefine Behavior, even on platforms where the only possible effects of the read attempt would be to yield the old value (indicating the hash code wasn't cached) or the last value written (which would always be the correct hash code).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文