C/C++的自增/自减操作符
我不喜欢用++
和--
运算符。主要是他们的各种执行顺序让我觉得很懵,而且早期C/C++标准对它们行为定义不严格,所以很容易触发Undefined Behavior。
不过,最近接触到了C++的atomic
类,不可避免地用到了自增/自减操作符,所以借此机会补一下知识盲区。
编译器的警告
首先,现在的编译器可以检测出自增/自减导致的UB。下面这三行代码全都是UB:
1 |
|
GCC会抛出一系列警告:
1 |
|
所以任何讨论上述代码运行结果是什么的问题,都是无意义的。
为什么是UB?
首先我知道,在一条语句里(严格来说是两个顺序点之间)不能对一个变量赋值两次,所以上面i = i++
这种写法是错误的。
但是,上面的第二行代码也是UB:
1 |
|
乍一看,只有i++
修改了一次i
的值。你可能会觉得,程序会先执行b=i+i
,最后自增i
。但是标准里规定,如果在两个顺序点之间对一个变量又读又写,则该变量的旧值要全部用于计算变量的新值。在上面里,i
的新值是i++
决定的,和第一个i
无关,所以该语句非法。
自增/自减的重载
在使用atomic
变量或者迭代器的时候,自增/自减操作符会很常用。
++
的重载是使用类似于下面的定义:
1 |
|
自增是单操作数的运算符,但是后自增却多了一个int
参数。这算是一种折中的策略。如果没有这个int,那么前自增和后自增的函数头完全一样,就没办法区分了。对于这个“假参数”,编译器会自动传一个0进去,所以我们不会看到obj++1
这样的代码。
造火箭
关于自增自减操作符,我看到过很多让我抓狂但偏偏又没错的写法。此处列举几个我见到过的例子。
*p++
在Linux内核里我经常看到类似*p++
这样的写法。这样的代码要如何解释呢?
规则:自增/自减运算符的优先级高于解引用。
因此p
先与++
结合,但后自增返回的是旧值,所以解引用解的是p
的旧值。
map.erase(it++)
参考文章
为什么在 C 语言中,i=1;i=(++i)+(++i)+(++i)+(++i); 得到 i 的结果是 15 而不是 14 ? - 知乎
c++ - Undefined behavior and sequence points - Stack Overflow
自增(++)自减(–)操作符 - C++操作符重载 - shadow_xwl的CSDN博客
c++ - Why use int as an argument for post-increment operator overload? - Stack Overflow