什么是"序列点"?
未定义的行为和序列点之间的关系是什么?
我经常使用有趣和复杂的表达方式a[++i] = i;,让自己感觉更好.我为什么要停止使用它们?
如果您已阅读此内容,请务必访问后续问题重新加载未定义的行为和序列点.
(注意:这是Stack Overflow的C++常见问题解答的一个条目.如果你想批评在这种形式下提供常见问题解答的想法,那么发布所有这些的元数据的发布将是这样做的地方.这个问题在C++聊天室中受到监控,其中FAQ的想法一开始就出现了,所以你的答案很可能被那些提出想法的人阅读.)
#include <stdio.h>
int main(void)
{
int i = 0;
i = i++ + ++i;
printf("%d\n", i); // 3
i = 1;
i = (i++);
printf("%d\n", i); // 2 Should be 1, no ?
volatile int u = 0;
u = u++ + ++u;
printf("%d\n", u); // 1
u = 1;
u = (u++);
printf("%d\n", u); // 2 Should also be one, no ?
register int v = 0;
v = v++ + ++v;
printf("%d\n", v); // 3 (Should be the …Run Code Online (Sandbox Code Playgroud) c increment operator-precedence undefined-behavior sequence-points
将此主题视为以下主题的续篇:
上一部分
未定义的行为和序列点
让我们重新审视这个有趣而复杂的表达(斜体短语取自上述主题*smile*):
i += ++i;
Run Code Online (Sandbox Code Playgroud)
我们说这会调用undefined-behavior.我假定说这个的时候,我们隐含假设型的i是内置的类型之一.
如果什么类型的i是用户定义类型?比如它的类型是Index在本文后面定义的(见下文).它还会调用未定义的行为吗?
如果是,为什么?它不等同于写作i.operator+=(i.operator++());甚至语法上更简单 i.add(i.inc());吗?或者,他们是否也调用未定义的行为?
如果不是,为什么不呢?毕竟,对象在连续的序列点之间i被修改两次.请回想一下经验法则:表达式只能在连续的"序列点"之间修改一个对象的值.如果 i += ++i是表达式,那么它必须调用未定义的行为.如果是,那么它的等价物i.operator+=(i.operator++());也 i.add(i.inc());必须调用undefined-behavior似乎是不真实的!(据我所知)
或者,i += ++i不是一个开头的表达?如果是这样,那么它是什么以及表达式的定义是什么?
如果它是一个表达式,并在同一时间,其行为也是定义良好的,那么就意味着与表达相关序列点的数量在某种程度上取决于该类型的参与表达操作数.我是否正确(甚至部分)?
顺便问一下,这个表达怎么样?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the …Run Code Online (Sandbox Code Playgroud) 术语"运算符优先级"和"评估顺序"是编程中非常常用的术语,对于程序员来说非常重要.而且,据我所知,这两个概念紧密相连; 在谈论表达时,一个人离不开另一个人.
我们举一个简单的例子:
int a=1; // Line 1
a = a++ + ++a; // Line 2
printf("%d",a); // Line 3
Run Code Online (Sandbox Code Playgroud)
现在,很明显会Line 2导致未定义的行为,因为C和C++中的序列点包括:
在评估&&(逻辑AND)的左右操作数之间,|| (逻辑OR)和逗号运算符.例如,在表达式中
*p++ != 0 && *q++ != 0,子表达式的所有副作用*p++ != 0都在任何访问尝试之前完成q.在评估三元"问号"运算符的第一个操作数和第二个或第三个操作数之间.例如,在表达式中,在第
a = (*p++) ? (*p++) : 0一个之后存在一个序列点*p++,这意味着它已经在第二个实例执行时增加了.在完整表达结束时.此类别包括表达式语句(例如赋值
a=b;),return语句,if,switch,while或do-while语句的控制表达式,以及for语句中的所有三个表达式.在函数调用中输入函数之前.未指定参数的计算顺序,但此序列点表示在输入函数之前所有副作用都已完成.在表达式中
f(i++) + g(j++) + h(k++),f使用原始值的参数调用i,但i在进入正文之前递增f.同样,j和k正在进入前更新g和h分别.然而,不指定以何种顺序f() …
几天前,这里有一个关于表达方式的讨论
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?有人能给出另一个有效的解释吗
谢谢!
很抱歉再次打开这个话题,但是考虑这个话题本身已经开始给我一个未定义的行为.想要进入定义明确的行为区域.
特定
int i = 0;
int v[10];
i = ++i; //Expr1
i = i++; //Expr2
++ ++i; //Expr3
i = v[i++]; //Expr4
Run Code Online (Sandbox Code Playgroud)
我认为上面的表达式(按此顺序)为
operator=(i, operator++(i)) ; //Expr1 equivalent
operator=(i, operator++(i, 0)) ; //Expr2 equivalent
operator++(operator++(i)) ; //Expr3 equivalent
operator=(i, operator[](operator++(i, 0)); //Expr4 equivalent
Run Code Online (Sandbox Code Playgroud)
现在来到这里的行为是来自C++ 0x的重要引用.
$ 1.9/12-"表达式(或子表达式)的评估通常包括值计算(包括确定用于左值评估的对象的身份以及获取先前分配给对象以进行右值评估的值)和启动副作用".
$ 1.9/15-"如果标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值计算的值没有排序,则行为未定义."
[注意:与不同参数表达式相关的值计算和副作用未被排序. - 尾注]
$ 3.9/9-"算术类型(3.9.1),枚举类型,指针类型,指向成员类型的指针(3.9.2),std :: nullptr_t和这些类型的cv限定版本(3.9.3)统称为标量类型."
在Expr1中,对表达式i(第一个参数)的评估在对评估operator++(i)(具有副作用)方面没有考虑.
因此,Expr1具有未定义的行为.
在Expr2中,对表达式i(第一个参数)的评估在对评估operator++(i, 0)(具有副作用)的评估方面没有考虑.
因此,Expr2具有未定义的行为.
在Expr3中,在调用operator++(i)外部之前,必须完成对单个参数的评估operator++.
因此,Expr3具有良好定义的行为. …
我知道写点东西
++a = a++;
Run Code Online (Sandbox Code Playgroud)
不仅是不可读的,而且还违反了c/c ++序列点.
这些限制来自哪里?在将它们视为错误之前,如何才能看到这些"问题"?
post增量运算符何时影响增量?我遇到了两个意见:
1)来自http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_015.htm:
POST表示在任何赋值操作后执行操作.
...将增量延迟到表达式结束(下一个序列点).
后增量操作也是如此......
A)等到达到序列点或
B)发布任务运营商或
C)在序列点之前的任何时间发生?