面试常碰到++p/p--问题到底结果是什么?

10 篇文章 0 订阅

下面代码输出结果是什么?

int p = 5;
cout<<++p/p--<<endl;

是不是觉得面熟? 😄,看到这个题目的同学,想必其中很多人在笔试面试过程中都碰到过这个问题,但究竟结果是多少呢?
也许你心里面已经有了答案!
1?++p 结果是6, 6/5 再强制转成int, 那不就是1嘛!
结果真的是这样吗?下面是是实际运行的结果。

root@LAPTOP-GV31B6PG:~/code/cpp/tmp$ cat main.cpp
#include <iostream>
using namespace std;

int main()
{
        int p = 5;
        cout<<++p/p--<<endl;
        return 0;
}

root@LAPTOP-GV31B6PG:~/code/cpp/tmp$ g++ -g main.cpp -o main
root@LAPTOP-GV31B6PG:~/code/cpp/tmp$ ./main
0

结果是0,是不是出乎意料,到底为什么呢?

下面带大家分析一下这个问题。自古:靠山,山会崩;靠地,地会陷;靠人,人会走。 人一辈子,靠天,靠地,不如靠自己;求天,求地,不如求自己。是的,必须要自己弄懂才行,要弄懂就得多动手!
下面的分析会用到gdb,汇编的知识,因此先给大家科普一下基础知识,如果对此熟悉的请略过这一段。

项目
AX――累加器(Accumulator),使用频度最高
AX――累加器(Accumulator),使用频度最高
BX――基址寄存器(Base Register),常存放存储器地址
CX――计数器(Count Register),常作为计数器
DX――数据寄存器(Data Register),存放数据
SI――源变址寄存器(Source Index),常保存存储单元地址
DI――目的变址寄存器(Destination Index),常保存存储单元地址
BP――基址指针寄存器(Base Pointer),表示堆栈区域中的基地址
SP――堆栈指针寄存器(Stack Pointer),指示堆栈区域的栈顶地址
IP――指令指针寄存器(Instruction Pointer),指示要执行指令所在存储单元的地址。IP寄存器是一个专用寄存器。
(gdb) disassemble /rm
Dump of assembler code for function main():
5       {
   0x000000000800088a <+0>:     55      push   %rbp
   0x000000000800088b <+1>:     48 89 e5        mov    %rsp,%rbp
   0x000000000800088e <+4>:     48 83 ec 10     sub    $0x10,%rsp

6               int p = 5;
=> 0x0000000008000892 <+8>:     c7 45 fc 05 00 00 00    movl   $0x5,-0x4(%rbp)

7               cout<<++p/p--<<endl;
   0x0000000008000899 <+15>:    83 45 fc 01     addl   $0x1,-0x4(%rbp)
   0x000000000800089d <+19>:    8b 4d fc        mov    -0x4(%rbp),%ecx
   0x00000000080008a0 <+22>:    8d 41 ff        lea    -0x1(%rcx),%eax
   0x00000000080008a3 <+25>:    89 45 fc        mov    %eax,-0x4(%rbp)
   0x00000000080008a6 <+28>:    8b 45 fc        mov    -0x4(%rbp),%eax
   0x00000000080008a9 <+31>:    99      cltd
   0x00000000080008aa <+32>:    f7 f9   idiv   %ecx
   0x00000000080008ac <+34>:    89 c6   mov    %eax,%esi
   0x00000000080008ae <+36>:    48 8d 3d 6b 07 20 00    lea    0x20076b(%rip),%rdi        # 0x8201020 <_ZSt4cout@@GLIBCXX_3.4>
   0x00000000080008b5 <+43>:    e8 a6 fe ff ff  callq  0x8000760 <_ZNSolsEi@plt>
   0x00000000080008ba <+48>:    48 89 c2        mov    %rax,%rdx
   0x00000000080008bd <+51>:    48 8b 05 0c 07 20 00    mov    0x20070c(%rip),%rax        # 0x8200fd0
   0x00000000080008c4 <+58>:    48 89 c6        mov    %rax,%rsi
   0x00000000080008c7 <+61>:    48 89 d7        mov    %rdx,%rdi
   0x00000000080008ca <+64>:    e8 71 fe ff ff  callq  0x8000740 <_ZNSolsEPFRSoS_E@plt>

8               return 0;
   0x00000000080008cf <+69>:    b8 00 00 00 00  mov    $0x0,%eax
---Type <return> to continue, or q <return> to quit---

9       }
   0x00000000080008d4 <+74>:    c9      leaveq
   0x00000000080008d5 <+75>:    c3      retq

End of assembler dump.
(gdb)
  • 命令disassemble /rm 显示反汇编, /r按照16进制显示数值,/m 显示源代码与汇编代码对应
  • 前三句初始配置函数栈帧
	0x000000000800088a <+0>:     55      push   %rbp  
  0x000000000800088b <+1>:     48 89 e5        mov    %rsp,%rbp
  0x000000000800088e <+4>:     48 83 ec 10     sub    $0x10,%rsp
  • 这段揭示了真实面目
7               cout<<++p/p--<<endl;
   0x0000000008000899 <+15>:    83 45 fc 01     addl   $0x1,-0x4(%rbp)
   0x000000000800089d <+19>:    8b 4d fc        mov    -0x4(%rbp),%ecx
   0x00000000080008a0 <+22>:    8d 41 ff        lea    -0x1(%rcx),%eax
   0x00000000080008a3 <+25>:    89 45 fc        mov    %eax,-0x4(%rbp)
   0x00000000080008a6 <+28>:    8b 45 fc        mov    -0x4(%rbp),%eax
   0x00000000080008a9 <+31>:    99      cltd
   0x00000000080008aa <+32>:    f7 f9   idiv   %ecx

mov -0x4(%rbp),%ecx 将上一句自增的值复制到寄存器%ecx, 注意到后面idiv除法运算时候这是作为分母值,再看分子lea -0x1(%rcx),%eax%rcx值(也就是%ecx)6 - 0x1即5赋值给 %eax 分子的5。

这一步就是揭示了++p/p--的值也就是5/6取整后就是0,这下真相大白了。

总结

通过以上分析,使用gdb调试功能很大程度上帮助我们分析一些看似不好理解的表达式。例如++p/p--那样,最终的计算实际是5/6的结果,取整输出后就是0。

微信公众号
第一时间获取最新内容,欢迎关注微信公众号:「程序员阿广」。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值