C语言中副作用和序列点(关于自增自减)

自增自减运算符很常见,先后关系大家一定都知道,但是在某些情况下可能会变的有些复杂,这对于初学者造成很大困扰,《C primer plus》一书中“副作用和序列点”这一节比较清晰的说明了特殊情况的运算顺序。

副作用:

C语言中副作用就是对数据对象或文件的修改。从C语言的角度上来看,它的主要目的是对表达式求值,至于赋值,修改这些对于我们来说的主要目的在C语言来看都是副作用。

例如,

调用printf函数,它的主要目的就是返回值。而在控制台显示是他的副作用。

对于a = 1;这条语句,它的主要目的也就是对该表达式求值,求出是1。至于将1赋值给a则是副作用。

我的看法是将语句和自定义的函数类比,函数体内我们实现各种功能,最后返回一个值,这个返回的值就是主要目的,其余的实现的功能就是副作用。(这句纯粹自己的理解)

序列点:

序列点是程序执行的点,在该点上所有的副作用都要在进入下一步之前完成。

C语言中,语句的分号标记了一个序列点。任何一个完整的表达式的结束也是一个序列点。还有一些运算符也有序列点。

推荐一篇博客《C语言中的序列点》

什么是完整表达式呢?

所谓完整表达式,就是指这个表达式不是另一个更大表达式的子表达式。例如,表达式语句中的表达式和while循环中作为测试的表达式都是完整表达式。

所以:

C 语言

#include <stdio.h>

int main(void) {
    int a = 5;
    while(a-->2) {
        printf("%d\n",a);
    }
    int b = 3;
    if(b-->2){
        printf("%d\n",b);
    }
}
/*
 输出:
 4
 3
 2
 2
 */
这里是先算(a-->2)这个完整表达式,当进入循环时,已经退出了这个序列点了,所以所有的副作用都要实现,所以a的值就改变了。

我们在看看其他语言:

C++:

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    int a = 5;
    while(a-->2){
        cout<<a<<endl;
    }
    int b = 3;
    if(b-->2){
        cout<<b<<endl;
    }
}
/*
 输出:
 4
 3
 2
 2
 */

JavaScript:

    let a = 5;
    while (a-- > 2) {
        console.log(a);
    }
    let b = 3;
    if (b-- > 2) {
        console.log(b);
    }
    /*
    输出:
    4
    3
    2
    2
    */



java:

public class Demo {
    public static void main(String[] args) {
        int a = 5;
        while(a-->2) {
            System.out.println(a);
        }
        int b = 3;
        if(b-->2) {
            System.out.println(b);
        }
    }
}
/**
 * 输出:
 * 4
 * 3
 * 2
 * 2
 */



测试到这里,上述语言都将循环和条件判断中的判断语句视为完整表达式。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章