表达式的定义行为
C99 标准表示为 6.5.2 美元。
在上一个和下一个序列点之间,对象应具有其存储值 通过表达式的求值最多修改一次。此外,先验值 应只读以确定要存储的值。
(我强调)
接下来要注意的是,以下示例是有效的(乍一看似乎很明显),
a[i] = i;
但它没有明确说明 a
和 i
是什么。
虽然我相信不会,但我想知道这个示例是否涵盖以下情况:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
第一句话:
已经足够清楚了。该语言不会对子表达式强加计算顺序,除非子表达式之间存在序列点,并且不需要某种未指定的计算顺序,它表示两次修改对象会产生未定义的行为。这允许积极的优化,同时仍然可以编写遵循规则的代码。
下一句话:
乍一看(和第二次)看起来确实不直观;为什么读取值的目的会影响表达式是否具有已定义的行为?
但它反映的是,如果子表达式 B 依赖于子表达式 A 的结果,则必须在计算 B 之前计算 A。 C90和C99标准没有明确说明这一点。
脚注中的示例给出了对该句子的更明显的违反:
假设
a
是声明的数组对象,而i
是声明的整数对象(没有指针或宏欺骗),没有对象被修改超过一次,所以它不违反第一句话。但是 LHS 上的i++
计算决定了要修改哪个对象,而 RHS 上的i
计算决定了要存储在该对象中的值 - 并且右侧读取操作和左侧写入操作的相对顺序未定义。同样,该语言可能要求以某种未指定的顺序计算子表达式,但它却保留了整个行为未定义,以允许更积极的优化。在您的示例中:
读取
i
的先前值以确定要存储的值和以确定它将存储在哪个对象中。 [i] 引用i
(但只是因为i==0
),修改i
的值会改变对象左值a[i]
所指的。在这种情况下,i
中存储的值与已存储在其中的值 (0
) 相同,但标准并没有例外:碰巧存储相同值的商店。我相信这种行为是未定义的。 (当然,标准中的示例并不是为了涵盖这种情况;它隐式假设a
是一个与i
无关的已声明数组对象。)标准规定是允许的:
可以将标准解释为未定义。但我认为第二句话指的是“先前值”,仅适用于由表达式修改的对象的值。
i
永远不会被表达式修改,因此不存在冲突。i
的值既用于确定要通过赋值修改的对象,也用于确定要存储在那里的值,但这没关系,因为i
本身的值永远不会改变。i
的值不是“先前值”,它只是值。C11标准为这种表达评估提供了一个新模型——或者更确切地说,它用不同的单词表达相同的模型。它不是“序列点”,而是谈论在彼此之前或之后排序的副作用,或者相对于彼此不排序的副作用。它明确表明,如果子表达式 B 依赖于子表达式 A 的结果,则必须在计算 B 之前先计算 A。
在N1570 草案中,第 6.5 节说:
The first sentence:
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:
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:
Assuming that
a
is a declared array object andi
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 ofi++
on the LHS determines which object is to be modified, and the evaluation ofi
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:
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. Sincea[i]
refers toi
(but only becausei==0
), modifying the value ofi
would change the object to which the lvaluea[i]
refers. It happens in this case that the value stored ini
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 thata
is a declared array object unrelated toi
.)As for the example that the standard says is allowed:
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 ofi
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 ofi
itself never changes. The value ofi
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:
读取对象的值来确定将其存储在何处不算“确定要存储的值”。这意味着唯一的争论点可能是我们是否正在“修改”对象
i
:如果是,则它是未定义的;如果是,则它是未定义的。如果不是,也没关系。将值
0
存储到已包含值0
的对象中是否算作“修改存储的值”?根据“修改”的简单英语定义,我不得不说不是;保持不变与修改它相反。然而,很明显,这将是未定义的行为:
毫无疑问,读取存储的值的目的不是确定要存储的值(要存储的值是常量),并且
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 value0
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:
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.