Pra*_*rav 22 c c++ expression language-lawyer
几天前,这里有一个关于表达方式的讨论
i = ++ i + 1
是否调用UB(未定义的行为).
最后得出的结论是它调用UB,因为'i'的值在两个序列点之间不止一次变化.
我参与了与Johannes Schaub在同一个线程中的讨论.据他介绍
i =(i,i ++,i)+1 ------(1)/*也调用UB*/
我说(1),因为先前的子表达式的副作用是由逗号运算符清零不调用UB""之间i和i ++和我++和我之间.
然后他给出了以下解释:
"是的,在i ++完成之前的所有副作用之后的序列点,但没有任何东西可以阻止赋值副作用与i ++的副作用重叠.潜在的问题是赋值的副作用未指定发生在或之后在评估赋值的两个操作数之前,所以序列点在保护它时无法做任何事情:序列点诱导偏序:只是因为在i ++之后和之前有一个序列点并不意味着所有的副作用都被排序关于我.
另外,请注意,只有序列点没有任何意义:评估的顺序不是由代码的形式决定的.它由语义规则决定.在这种情况下,没有语义规则说明何时在评估那些操作数的操作数或子表达式时发生赋值副作用".
用"大胆"写的声明使我感到困惑.我所知道的:
"在执行序列中称为序列点的某些特定点,先前评估的所有副作用都应完整,并且不会产生后续评估的副作用."
因为,逗号运算符也指定了执行顺序,当我们到达最后一个i时,i ++的副作用已被取消.如果未指定评估顺序,那么(Johannes)会是正确的(但是在逗号运算符的情况下,它已被明确指定) ).
所以我只想知道(1)是否调用UB?有人能给出另一个有效的解释吗
谢谢!
Mic*_*urr 15
C标准说明了关于赋值运算符(C90 6.3.16或C99 6.5.16赋值运算符):
更新左操作数的存储值的副作用应发生在前一个和下一个序列点之间.
在我看来,在声明中:
i=(i,i++,i)+1;
Run Code Online (Sandbox Code Playgroud)
赋值运算符的序列点'previous'将是第二个逗号运算符,'next'序列点将是表达式的结尾.所以我要说表达式不会调用未定义的行为.
但是,这个表达式:
*(some_ptr + i) = (i,i++,i)+1;
Run Code Online (Sandbox Code Playgroud)
会有未定义的行为,因为赋值运算符的2个操作数的求值顺序是未定义的,在这种情况下,而不是当赋值运算符的副作用发生时的问题,问题是你不知道是否值的我在左侧手柄中使用的操作数将在右侧之前或之后进行评估.这个评估问题的顺序在第一个例子中没有出现,因为在该表达式中,值的i实际上并未在左侧使用 - 赋值运算符所关注的全部是"左值" i.
但我也认为所有这些都足够粗略(而且我对所涉及的细微差别的理解已经足够粗略),如果有人能说服我,我也不会感到惊讶(无论如何).
我相信以下表达式肯定有未定义的行为.
i + ((i, i++, i) + 1)
Run Code Online (Sandbox Code Playgroud)
原因是逗号运算符指定括号中子表达式之间的序列点,但未指定该序列中左手操作数的评估+发生的位置.一种可能性是在周围的序列点之间i++,这违反了i写入两个序列点之间的5/4,但是在相同的序列点之间也被读取两次,而不仅仅是确定要存储的值而且还要确定值.运算+符的第一个操作数.
这也有不确定的行为.
i += (i, i++, i) + 1;
Run Code Online (Sandbox Code Playgroud)
现在,我对这个说法不太确定.
i = (i, i++, i) + 1;
Run Code Online (Sandbox Code Playgroud)
虽然相同的原理适用,但i必须"评估"为可修改的左值,并且可以随时进行,但我不相信它的价值是作为其中的一部分阅读的.(或者是否存在表达式违反导致UB的另一个限制?)
子表达式(i, i++, i)作为确定要存储的值的一部分发生,并且该子表达式包含在存储值之后的序列点i.i++在确定要存储的值之前,我没有看到任何方式不需要完成副作用,因此可能发生分配副作用的最早可能点.
在此sequnce点i的值最多只读取一次并且仅确定将存储回的值i,因此最后一部分是正常的.
| 归档时间: |
|
| 查看次数: |
1673 次 |
| 最近记录: |