前置++ 和后置++ 的思考

253 阅读2分钟

如果想成为一个合格的C++程序员,必须对你所操作的每个字节的去处以及内存的操作都有个清晰的认知。

前置++ 和后置++ 有何区别?

让我们来验证一下,新建两个文件b.cppc.cpp

 //b.cpp
 int main(){
     int a =0;
     ++a;
     return 0;
 }

//c.cpp
 int main(){
     int a =0;
     a++;
     return 0;
 }

然后生成汇编文件 g++ -S c.cpp -o c.sg++ -S b.cpp -o b.s

//b.s
 main:
 .LFB0:
     pushq   %rbp
     movq    %rsp, %rbp
     movl    $0, -4(%rbp)
     addl    $1, -4(%rbp)
     movl    $0, %eax
     popq    %rbp
     ret
     
 //c.s
  main:
 .LFB0:
     pushq   %rbp
     movq    %rsp, %rbp
     movl    $0, -4(%rbp)
     addl    $1, -4(%rbp)
     movl    $0, %eax
     popq    %rbp
     ret

通过这个例子可以发现他们是一毛一样的。本质上没什么区别的。

真的是这样的吗?

我们再来一个例子,稍微修改前面的例子即可

//b.cpp 
int main(){
     int a =0; 
     (++a)+1; // 这里修改了
     return 0;
 }
//c.cpp
 int main(){
     int a =0;
     (a++)+1; // 这里修改了
     return 0;
 }

然后只编译不汇编的方式,生成汇编文件:

 //b.s
 main:
 .LFB0:
     pushq   %rbp
     movq    %rsp, %rbp
     movl    $0, -4(%rbp)
     addl    $1, -4(%rbp)
     movl    $0, %eax
     popq    %rbp
     ret 
     
 //c.s
  main:
 .LFB0:
     pushq   %rbp
     movq    %rsp, %rbp
     movl    $0, -4(%rbp)
     movl    -4(%rbp), %eax
     addl    $1, %eax
     movl    %eax, -4(%rbp)
     movl    $0, %eax
     popq    %rbp
     ret

可以发现有些许区别:多了两次 movl 是不是可以证明拷贝了两次。

事实可以证明:由于部分编译器会优化,所以导致示例一没有区别,但这一行为还是和编译器的行为相关,而示例二表明后置++ 会导致多拷贝了两次。

此处建议:所有可以使用前置的都使用前置++(毕竟我们不能写出依赖编译器行为的代码) ,除非在一些特殊的场景必须使用后置++ 。比方说:

  1. 如果是从集合中剔除一项
  2. 指针的偏移 *(pa++)

所以如果有写 for(size_t i=0 ;i<6;i++) 的这种情况,是不可取的。

源码放置于github

每天分享一点编程小知识,欢迎关注我的微信公众号:

程序员学习分享录