C++自增与自减 C++中的自增与自减
梁唐 人气:0自增与自减是C++当中两个使用频率非常高的运算符,不仅在循环当中用到,在日常的代码当中也经常使用。
1、自增与自减
1.1 基本用法
自增与自减是C++当中两个使用频率非常高的运算符,不仅在循环当中用到,在日常的代码当中也经常使用。
甚至C++这个名称的由来都和自增运算符有关,表示C语言的升级版。当然这也是C#名字的由来,#这个符号表示4个叠加的加号……不得不吐槽这微软的恶趣味。
我们都知道自增有两种写法,一种是i++
另外一种是++i
。这两种写法对于i这个变量的最终结果来说是一样的,都是自增了1,但是对于自增这个操作的发生时间,则有很大的差异。
比如:
int a = 0, b = 0; cout << a++ << endl; cout << ++b << endl;
最终我们得到的输出结果是0和1,差别就在执行自增的时间。对于cout << a++
来说,它是先执行cout操作,再执行自增,而cout << ++b
则相反,是先执行自增再执行cout
。
同理,我们在赋值的时候也是一样:
int a = 0, b = 0; int c = a++; int d = ++b;
c和d得到的结果同样是一个为0,另外一个为1,原因和刚才一样。
以上的规则同样适用于自减。
1.2 进阶理解
现在我们知道了++i的执行顺序在i++之前,那么问题来了,那么它们两者的执行顺序究竟是怎样的?差异到底在哪里呢?
对此,C++当中有一个叫做顺序点的概念,顺序点指的是程序执行过程中的一个点。在C++当中语句中的分号就是一个顺序点,在程序处理下一条语句之前,赋值运算符、自增、自减运算符执行的所有修改都必须完成。除了分号之外,完整的表达式末尾也是一个顺序点。
完整表达式的概念有点费解,C++ Primer
中的定义是不是另一个更大的表达式的子表达式,比如while
循环中的检测语句就是一个完整表达式。
比如:
int cnt = 0; while (cnt++ < 10) cout << cnt << endl;
程序的输出结果是:
我们可以看到它的输出结果从1开始,而并非从0开始。意味着我们在执行cout
之前,cnt
变量就已经完成了自增。这进一步说明了while(cnt++ < 10)
本身就已经是一个完整表达式了。因此在这个表达式执行之前,C++
就会完成自增的操作。
关于完整表达式还有一个坑点,就是它的执行顺序。
比如下面这个例子:
y = (4 + x++) * (6 + x++);
由于(4 + x++)
和(6 + x++)
都不是一个完整表达式,因此C++
并不能保证x++
的执行顺序,它没有规定是在每个子表达式计算之后执行自增,还是整个表达式计算之后再自增。它只能保证在执行到下一条语句之前x变量被自增两次,至于它的执行时间则无法保障。
因此,最好不要写出这样的代码,不仅可读性差,而且结果也可能不可靠。
1.3 差异
我们还有一个问题没有解决,在不影响结果的情况下,前缀的形式和后缀的形式究竟还有没有其他差别呢?
比如:
x++; ++x; for (int i = 0; i < n; i++); for (int i = 0; i < n; ++i);
我们现在知道它们的结果是一样的,但在内部执行是有细微差别的。差别在于后缀的形式会先生成一个拷贝值,再将拷贝值赋值给原值,而前缀的版本是直接在原值上修改。因此理论上来说,前缀版本的效率更高。当然这当中的差别非常细微,几乎可以忽略不计。
但是在面试当中很有可能会被问到,因此有所了解即可。
1.4 指针自增、自减
自增自减操作同样可以运用在指针上,前文当中介绍过,这表示指针的移动。自增表示向右移动一位,自减表示向左移动一位。
这很简单,但是当我们把一些操作符结合在一起就有些麻烦了。C++
当中规定,前缀运算符和解引用运算符优先级相同,按照从右到左的方式结合,后缀运算符优先级更高,从左至右。
这意味着*++pt
表示先执行指针自增操作,也就是移动一位之后,再解引用。
++*pt
则意味着先解引用取得值,再对改值加1。
x=*pt++
由于后缀符的优先级更高,意味着先执行指针移动,再解引用。如果大家实在搞不清楚的话,可以使用括号。
加载全部内容