C++11以前的序列点

首先看wiki:
https://en.wikipedia.org/wiki/Sequence_point

简单翻译:

C和C++中的序列点

在C和C++中,下列位置被认为是序列点。(注意,被重载的运算符,相当于函数调用,它们引入的序列点与运算符没有关系,而与函数调用一致)
1.&&(与)、||(或)运算符的左右两边之间有序列点。(为了实现短路逻辑)逗号表达式也是一个序列点。例如, *p++ != 0 && *q++ != 0, *p++!=0这个子表达式的所有副作用完成之后,才会对q进行计算。
2.问号运算符的第一个判断表达式之后,和第二个或者第三个语句之间。例如, a+(*p++)?(*p++):0,在第一个*p++之后有序列点,所以当执行第二个*p++时,第一个自增操作已经完成。
3.在所有的“全表达式”之后。包括表达式语句,如a=b;,和返回值语句,if,switch,while,do-while的控制表达式,以及for循环里的3个语句。
4.调用函数之前。但函数中多个参数表达式的执行顺序是未定义的,不过它们在进入函数调用栈之前肯定全部执行完了。在f(i++) + g(j++) + h(k++)中,f有个参数i,i在进入函数f之前已经自增完成。类似地,j和k也是同样。但是,f、g、h 3个函数调用以什么顺序发生,则是未定义的。导致i、j、k以什么顺序自增也变成未定义了。在函数f执行的时候,j和k可能已经自增,也可能没有。注意,f(a,b,c)中,逗号不是逗号表达式,只是参数的分隔符,因此a、b、c3个表达式的执行顺序是未定的。
5.函数返回,当返回值拷贝到调用栈后。(在C++标准中明确规定,在C中则只是隐含推论出的,未明确规定)
6.初始化结束知乎。如int a = 5;
7.多个声明。如int x=a++,y=a++;,这跟逗号表达式不一样,但也是序列点。
8.在I/O格式化中。例如,在printf(”foo %n %d”, &a, 42);在%n求值以后,尚未打印42之前,有一个序列点。

特别注意:
* f(i++) + g(j++) + h(k++) 三个函数执行顺序未定义
* printf(“%d, %d, %d”, a++, –b, a++), 3个参数表达式执行顺序未定义
* cout << a++ << b++ << c++; 3个表达式的顺序也未定义。因为相当于cout.operator<<(a++). operator<<(b++). operator<<(c++),又相当于operator<<( operator<<( operator<<(cout, a++), b++), c++),<<相当于两个参数的函数。
典型的未定义行为:
Undefined behavior
1) If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.两个序列点之间,对同一个变量只能写一次。多次修改会导致未定义行为。
i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior
2) If a side effect on a scalar object is unsequenced relative to a value computation using the value of the same scalar object, the behavior is undefined.两个序列点之间,对同一个变量又读又写,那么也是未定义的。因为不知道读先发生还是写先发生。
f(i, i++); // undefined behavior
a[i] = i++; // undefined bevahior

在单例模式的double check locking中,之所以会出错,就是因为p = new T;这个语句结束时有个序列点,但未结束之前,赋值和构造谁先发生,就是未定义的了。

你可能感兴趣的:(C++)