目录
1. 副作用(side effect)
2.??序列点(sequence point)
遵循原则:
?了解序列点之前先来看下副作用:
1. 副作用(side effect)
看下边代码:
int factorial(int n)
{
int result = 1;
int i;
for(i = 1; i <= n; ++i)
result = result * i;
return result;
}
?其中++i这个表达式相当于i = i + 1[9],++称为前缀自增运算符(Prefix Increment Operator), 类似地,--称为前缀自减运算符(Prefix Decrement Operator)[10],--i相当于i = i - 1。如果 把++i这个表达式看作一个函数调用,除了传入一个参数返回一个值(等于参数值加1)之外,还产 生一个Side Effect,就是把变量i的值增加了1。 ++和--运算符也可以用在变量后面,例如i++和i--,为了和前缀运算符区别,这两个运算符称为后 缀自增运算符(Postfix Increment Operator)和后缀自减运算符(Postfix Decrement Operator)。如果把i++这个表达式看作一个函数调用,传入一个参数返回一个值,返回值就等于 参数值(而不是参数值加1),此外也产生一个Side Effect,就是把变量i的值增加了1,它和++i的 区别就在于返回值不同。同理,--i返回减1之后的值,而i--返回减1之前的值,但这两个表达式都 产生同样的Side Effect,就是把变量i的值减了1。
2.??序列点(sequence point)
int main(void)
{
int a = 2, b = 3, c;
//a = a++;
//c = a+++++a; //error
c = a++ + ++a; // 2+4 or 3+3 ?
printf("a:%d b:%d c:%d\n", a, b, c);
printf("a:%d a++:%d\n", a, a++);
printf("b:%d ++b:%d\n", b, ++b);
printf("c:%d c++:%d ++c:%d\n", c, c++, ++c);
printf("c:%d ++c:%d c++:%d\n", c, ++c, c++);
return 0;
}
加上gcc 加上-Wall编译:
?
执行结果(注意这个结果跟编译器相关):

?
各种Sequence Point?
1、调用一个函数时,在所有准备工作做完之后、函数调用开始之前是Sequence Point。比如调 用foo(f(), g())时,foo、f()、g()这三个表达式哪个先求值哪个后求值是Unspecified,但是必须 都求值完了才能做最后的函数调用,所以f()和g()的Side Effect按什么顺序发生不一定,但必定在 这些Side Effect全部作用完之后才开始调用foo函数。 2、条件运算符?:、逗号运算符、逻辑与&&、逻辑或||的第一个操作数求值之后是Sequence Point。我们刚讲过条件运算符和逗号运算符,条件运算符要根据表达式1的值是否为真决定下一步 求表达式2还是表达式3的值,如果决定求表达式2的值,表达式3就不会被求值了,反之也一样,逗 号运算符也是这样,表达式1求值结束才继续求表达式2的值。? ?? ?&&运算与此类似,a && b的计算过程是:首先求表达式a的值,如果a的值是假则整个表达式的值 是假,不会再去求b的值;如果a的值是真,则下一步求b的值作为整个表达式的值。所以,a && b相当于“if a then b”,而a || b相当于“if not a then b”。这种特性称为Short-circuit,很多人喜欢利 用Short-circuit特性简化代码。 3、在一个完整的声明末尾是Sequence Point,所谓完整的声明是指这个声明不是另外一个声明的 一部分。比如声明int a[10], b[20];,在a[10]末尾是Sequence Point,在b[20]末尾也是。 4、在一个完整的表达式末尾是Sequence Point,所谓完整的表达式是指这个表达式不是另外一个 表达式的一部分。所以如果有f(); g();这样两条语句,f()和g()是两个完整的表达式,f()的Side Effect必定在g()之前发生。 5、在库函数即将返回时是Sequence Point。这条规则似乎可以包含在上一条规则里面,因为函数 返回时必然会结束掉一个完整的表达式。而事实上很多库函数是以宏定义的形式实现的(第 2.1 节 “函数式宏定义”),并不是真正的函数,所以才需要有这条规则。 还有两种Sequence Point和某些C标准库函数的执行过程相关,此处从略,有兴趣的读者可参 考[C99]的Annex C。
3. 遵循原则:
写表达式应遵循的原则一:在两个Sequence Point之间,同一个变量的值只允许被改变一次。仅 有这一条原则还不够,例如a[i++] = i;的变量i只改变了一次,但结果仍是Undefined,因为等号 左边改i的值,等号右边读i的值,到底是先改还是先读?这个读写顺序是不确定的。但为什么i = i + 1;就没有歧义呢?虽然也是等号左边改i的值,等号右边读i的值,但你不读出i的值就没法计 算i + 1,那拿什么去改i的值呢?所以这个读写顺序是确定的。写表达式应遵循的原则二:如果在 两个Sequence Point之间既要读一个变量的值又要改它的值,只有在读写顺序确定的情况下才可 以这么写。
另外可参考:
https://blog.csdn.net/tsroad/article/details/49834261
|