表达式的定义行为

发布于 2024-12-29 09:48:43 字数 567 浏览 2 评论 0原文

C99 标准表示为 6.5.2 美元。

在上一个和下一个序列点之间,对象应具有其存储值 通过表达式的求值最多修改一次。此外,先验值 应只读以确定要存储的值。

(我强调)

接下来要注意的是,以下示例是有效的(乍一看似乎很明显),

a[i] = i;

但它没有明确说明 ai 是什么。

虽然我相信不会,但我想知道这个示例是否涵盖以下情况:

int i = 0, *a = &i;
a[i] = i;

这将不会更改i的值,但访问<的值code>i 来确定放置值的地址。或者我们为已经存储在 i 中的 i 分配一个值是否无关紧要?请透露一些信息。


奖金问题; a[i]++a[i] = 1 怎么样?

The C99 Standard says in $6.5.2.

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

(emphasis by me)

It goes on to note, that the following example is valid (which seems obvious at first)

a[i] = i;

While it does not explicitly state what a and i are.

Although I believe it does not, I'd like to know whether this example covers the following case:

int i = 0, *a = &i;
a[i] = i;

This will not change the value of i, but access the value of i to determine the address where to put the value. Or is it irrelevant that we assign a value to i which is already stored in i? Please shed some light.


Bonus question; What about a[i]++ or a[i] = 1?

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

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

发布评论

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

评论(2

我很坚强 2025-01-05 09:48:43

第一句话:

在上一个和下一个序列点之间,对象应有其
存储的值最多可以通过表达式的求值修改一次。

已经足够清楚了。该语言不会对子表达式强加计算顺序,除非子表达式之间存在序列点,并且不需要某种未指定的计算顺序,它表示两次修改对象会产生未定义的行为。这允许积极的优化,同时仍然可以编写遵循规则的代码。

下一句话:

此外,先前的值应只读以确定要存储的值

乍一看(和第二次)看起来确实不直观;为什么读取值的目的会影响表达式是否具有已定义的行为?

但它反映的是,如果子表达式 B 依赖于子表达式 A 的结果,则必须在计算 B 之前计算 A。 C90和C99标准没有明确说明这一点。

脚注中的示例给出了对该句子的更明显的违反:

a[i++] = i; /* undefined behavior */

假设 a 是声明的数组对象,而 i 是声明的整数对象(没有指针或宏欺骗),没有对象被修改超过一次,所以它不违反第一句话。但是 LHS 上的 i++ 计算决定了要修改哪个对象,而 RHS 上的 i 计算决定了要存储在该对象中的值 - 并且右侧读取操作和左侧写入操作的相对顺序未定义。同样,该语言可能要求以某种未指定的顺序计算子表达式,但它却保留了整个行为未定义,以允许更积极的优化。

在您的示例中:

int i = 0, *a = &i;
a[i] = i; /* undefined behavior (I think) */

读取 i 的先前值以确定要存储的值以确定它将存储在哪个对象中。 [i] 引用 i (但只是因为 i==0),修改 i 的值会改变对象左值 a[i] 所指的。在这种情况下,i 中存储的值与已存储在其中的值 (0) 相同,但标准并没有例外:碰巧存储相同值的商店。我相信这种行为是未定义的。 (当然,标准中的示例并不是为了涵盖这种情况;它隐式假设 a 是一个与 i 无关的已声明数组对象。

)标准规定是允许的:

int a[10], i = 0; /* implicit, not stated in standard */
a[i] = i;

可以将标准解释为未定义。但我认为第二句话指的是“先前值”,仅适用于由表达式修改的对象的值。 i 永远不会被表达式修改,因此不存在冲突。 i 的值既用于确定要通过赋值修改的对象,也用于确定要存储在那里的值,但这没关系,因为 i 本身的值永远不会改变。 i 的值不是“先前值”,它只是值。

C11标准为这种表达评估提供了一个新模型——或者更确切地说,它用不同的单词表达相同的模型。它不是“序列点”,而是谈论在彼此之前或之后排序的副作用,或者相对于彼此不排序的副作用。它明确表明,如果子表达式 B 依赖于子表达式 A 的结果,则必须在计算 B 之前先计算 A。

N1570 草案中,第 6.5 节说:

1 表达式是一系列运算符和操作数
指定值的计算,或指定对象
或一个函数,或产生副作用,或执行
它们的组合。操作数的值计算
运算符的值计算之前被排序
运算符的结果。

2 如果标量对象上的副作用相对于
对同一标量对象产生不同的副作用或
使用同一标量对象的值进行值计算,
行为未定义。如果有多个允许的订购
表达式的子表达式的行为未定义
如果任何顺序中出现此类未排序的副作用。

3 运算符和操作数的分组由语法指示。
除非稍后指定,否则副作用和值计算
的子表达式是无序的。

The first sentence:

Between the previous and next sequence point an object shall have its
stored value modified at most once by the evaluation of an expression.

is clear enough. The language doesn't impose an order of evaluation on subexpressions unless there's a sequence point between them, and rather than requiring some unspecified order of evaluation, it says that modifying an object twice produces undefined behavior. This allows aggressive optimization while still making it possible to write code that follows the rules.

The next sentence:

Furthermore, the prior value shall be read only to determine the value to be stored

does seem unintuitive at first (and second) glance; why should the purpose for which a value is read affect whether an expression has defined behavior?

But what it reflects is that if a subexpression B depends on the result of a subexpression A, then A must be evaluated before B can be evaluated. The C90 and C99 standards do not state this explicitly.

A clearer violation of that sentence, given in an example in the footnote, is:

a[i++] = i; /* undefined behavior */

Assuming that a is a declared array object and i is a declared integer object (no pointer or macro trickery), no object is modified more than once, so it doesn't violate the first sentence. But the evaluation of i++ on the LHS determines which object is to be modified, and the evaluation of i on the RHS determines the value to be stored in that object -- and the relative order of the read operation on the RHS and the write operation on the LHS is not defined. Again, the language could have required the subexpressions to be evaluated in some unspecified order, but instead it left the entire behavior undefined, to permit more aggressive optimization.

In your example:

int i = 0, *a = &i;
a[i] = i; /* undefined behavior (I think) */

the previous value of i is read both to determine the value to be stored and to determine which object it's going to be stored in. Since a[i] refers to i (but only because i==0), modifying the value of i would change the object to which the lvalue a[i] refers. It happens in this case that the value stored in i is the same as the value that was already stored there (0), but the standard doesn't make an exception for stores that happen to store the same value. I believe the behavior is undefined. (Of course the example in the standard wasn't intended to cover this case; it implicitly assumes that a is a declared array object unrelated to i.)

As for the example that the standard says is allowed:

int a[10], i = 0; /* implicit, not stated in standard */
a[i] = i;

one could interpret the standard to say that it's undefined. But I think that the second sentence, referring to "the prior value", applies only to the value of an object that's modified by the expression. i is never modified by the expression, so there's no conflict. The value of i is used both to determine the object to be modified by the assignment, and the value to be stored there, but that's ok, since the value of i itself never changes. The value of i isn't "the prior value", it's just the value.

The C11 standard has a new model for this kind of expression evaluation -- or rather, it expresses the same model in different words. Rather than "sequence points", it talks about side effects being sequenced before or after each other, or unsequenced relative to each other. It makes explicit the idea that if a subexpression B depends on the result of a subexpression A, then A must be evaluated before B can be evaluated.

In the N1570 draft, section 6.5 says:

1 An expression is a sequence of operators and operands that
specifies computation of a value, or that designates an object
or a function, or that generates side effects, or that performs
a combination thereof. The value computations of the operands
of an operator are sequenced before the value computation of the
result of the operator.

2 If a side effect on a scalar object is unsequenced relative to
either a different side effect on the same scalar object or a
value computation using the value of the same scalar object, the
behavior is undefined. If there are multiple allowable orderings
of the subexpressions of an expression, the behavior is undefined
if such an unsequenced side effect occurs in any of the orderings.

3 The grouping of operators and operands is indicated by the syntax.
Except as specified later, side effects and value computations
of subexpressions are unsequenced.

甜味超标? 2025-01-05 09:48:43

读取对象的值来确定将其存储在何处不算“确定要存储的值”。这意味着唯一的争论点可能是我们是否正在“修改”对象i:如果是,则它是未定义的;如果是,则它是未定义的。如果不是,也没关系。

将值 0 存储到已包含值 0 的对象中是否算作“修改存储的值”?根据“修改”的简单英语定义,我不得不说不是;保持不变与修改它相反。

然而,很明显,这将是未定义的行为:

int i = 0, *a = &i;
a[i] = 1;

毫无疑问,读取存储的值的目的不是确定要存储的值(要存储的值是常量),并且i 的值被修改。

Reading an object's value to determine where to store it does not count as "determining the value to be stored". This means that the only point of contention can be whether or not we are "modifying" the object i: if we are, it is undefined; if we are not, it is OK.

Does storing the value 0 into an object which already contains the value 0 count as "modifying the stored value"? By the plain English definition of "modify" I would have to say not; leaving something unchanged is the opposite of modifying it.

It is, however, clear that this would be undefined behaviour:

int i = 0, *a = &i;
a[i] = 1;

Here there can be no doubt that the stored value is read for a purpose other than determining the value to be stored (the value to be stored is a constant), and that the value of i is modified.

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