• 胖胖胖
    2019-05-08
    个人理解:这一讲的核心在于理解几个寄存器的作用,从而理解cpu运行程序的过程:cpu从PC寄存器中取地址,找到地址对应的内存位子,取出其中指令送入指令寄存器执行,然后指令自增,重复操作。所以只要程序在内存中是连续存储的,就会顺序执行这也是冯诺依曼体系的理念吧。而实际上跳转指令就是当前指令修改了当前PC寄存器中所保存的下一条指令的地址,从而实现了跳转。当然各个寄存器实际上是由数电中的一个一个门电路组合出来的,而各个门电路的具体电路形式也是属于模电的东西。对于我们来说,有个具体概念就行,实在需要的时候再回去翻翻课本捡起来就行。

    作者回复: 👍完全正确。

    
     67
  • L
    2019-05-08
    非计算机专业 表示看到这一章已经很懵逼了

    作者回复: 那要加油搞清楚啊。

     1
     37
  • 不记年
    2019-05-10
    cpu的在执行指令时还要有个转码的电路来将指令转换成不同的电信号,这些电信号可以控制各个寄存器的动作~

    作者回复: 👍这个关于CPU的控制器的译码器的部分我会在后续讲解CPU的部分讲到。

    
     11
  • Out
    2019-05-09
    老师您好,在文中您提到:“在这里,如果比较的结果是False,也就是0,就把零标志码设置为1” 这个地方是不是有问题,根据我查到结果,cmp will ZF to 1 when two operands are equal. 所以如果比较的结果是True,才会把零标志码设置为1。

    作者回复: 是得,笔误了。应该是 “如果比较得结果是True,也就是 r == 0,就把零标志码设置为1”
    不然后面得jne跳转和这里也对不上。

    
     11
  • Linuxer
    2019-05-08
    int main()
    {
       0: 55 push rbp
       1: 48 89 e5 mov rbp,rsp
       int i = 0;
       4: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
       int a = 0;
       b: c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0
       switch(i)
      12: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
      15: 83 f8 01 cmp eax,0x1
      18: 74 07 je 21 <main+0x21>
      1a: 83 f8 02 cmp eax,0x2
      1d: 74 0b je 2a <main+0x2a>
      1f: eb 12 jmp 33 <main+0x33>
       {
          case 1:
            a = 1;
      21: c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1
            break;
      28: eb 11 jmp 3b <main+0x3b>
          case 2:
            a = 2;
      2a: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2
            break;
      31: eb 08 jmp 3b <main+0x3b>
          default:
            a = 3;
      33: c7 45 f8 03 00 00 00 mov DWORD PTR [rbp-0x8],0x3
            break;
      3a: 90 nop
       }

       return 1;
      3b: b8 01 00 00 00 mov eax,0x1
    }
      40: 5d pop rbp
      41: c3 ret

    课后问题验证,这么看如果是单纯的两个分支采用if else更有利,另外 mov eax,0x1从这儿看象是main的返回值
    展开

    作者回复: 是的,如果没有提供返回值,很多版本的编译器会隐式地生成一个return 0;的返回值,就会生成 mov eax, 0x0 的多出来的指令。我修改一下让文章更准确一点。

    
     8
  • aiter
    2019-05-09
    徐老师好~
    C语言我不会,。,努力看了半天,算是懂了大部分,但是for循环那里还是有点问题~汇编语言里,jmp 1e 之后,应该是做比较cmp,但是为什么不是0和3比较,而是和16进制的2(0x2)比较?
    ————-
    因为后面用的jle(jump if less or equal) <=2.如果是使用jl(jump if less) <3.应该是编译器的优化行为?可以自己写汇编代码,使用jl 0x3试试效果是否一样

    作者回复: aiter同学谢谢。我回复了,不过你这里的理解不太对,jle指令并不是和2做比较,而是判断标志位的,jle 和 jl 用的是不同的标志位,具体可以看看这个reference http://www.unixwiz.net/techtips/x86-jumps.html

    
     6
  • 大熊
    2019-05-22
    接testswitch1的那条
    如果条件比较多,3个以上,反汇编出来的代码就是先经过一系列计算,最后跳转。

    -----------testswitch2.c
    #include <stdio.h>

    int main(int argc, char const *argv[])
    {
        int m = 3;
        int a = 0;

        switch (m)
        {
        case 5:
            a = 5;
            break;
        case 4:
            a = 4;
            break;
        case 3:
            a = 3;
            break;
        case 2:
            a = 2;
            break;
        case 1:
            a = 1;
            break;
        default:
            a = 9;
        }
        return 0;
    }
    -----------testswitch2.c

    上面的代码进行的反汇编:

    -----------testswitch2
    #include <stdio.h>

    int main()
    {
       0: 55 push rbp
       1: 48 89 e5 mov rbp,rsp
       4: 89 7d ec mov DWORD PTR [rbp-0x14],edi
       7: 48 89 75 e0 mov QWORD PTR [rbp-0x20],rsi
        int m = 3;
       b: c7 45 f8 03 00 00 00 mov DWORD PTR [rbp-0x8],0x3 # 把3,放在地址[rbp-0x8]
        int a = 0;
      12: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 # 把0,放在地址[rbp-0x4]

        switch (m)
      19: 83 7d f8 05 cmp DWORD PTR [rbp-0x8],0x5 # 把[rbp-0x8]的值(即3)和5比较
      1d: 77 51 ja 70 <main+0x70> # 如果大于5,则跳转到70(即default)执行 a = 9
      1f: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] # 把3,放入eax

      # 从这里开始不做过多解释,这就是经过一系列操作和计算的部分
      22: 48 8d 14 85 00 00 00 lea rdx,[rax*4+0x0]
      29: 00
      2a: 48 8d 05 00 00 00 00 lea rax,[rip+0x0] # 31 <main+0x31>
      31: 8b 04 02 mov eax,DWORD PTR [rdx+rax*1] #
      34: 48 63 d0 movsxd rdx,eax
      37: 48 8d 05 00 00 00 00 lea rax,[rip+0x0] # 3e <main+0x3e>
      3e: 48 01 d0 add rax,rdx
      # 到这里结束
      41: ff e0 jmp rax # 直接执行的是jmp指令,直接跳转到要执行的语句位置
    /*后面代码省略*/
    -----------testswitch2

    所以,switch判断条件多,且最好case之间的差值不要过大的时候最好使用switch
    展开

    作者回复: 👍多动手自己体会是王道。

    
     4
  • 越努力,越幸运
    2019-05-15
    `而 [rbp-0x4] 则是一个寄存器的地址` 这个不应该是栈地址吗,rbp是栈基址,rbp - 0x4是第一个local var的内存地址
     3
     4
  • rookie
    2019-05-10
    程序如下:
    int main(){
        int i = 0;
        int a = 0;
        switch(i){
            case 1:
                a = 1;
                break;
            case 2:
                a = 2;
                break;
            default:
                a = 3;
                break;}
        return 1;
    }

    下面是机器码和汇编代码:
    0000000000000000 <main>:
       0:    55     push rbp
       1:    48 89 e5     mov rbp,rsp
       4:    c7 45 f8 00 00 00 00     mov DWORD PTR [rbp-0x8],0x0 #将0复制给[rbp-0x8] 这个内存地址,即 i = 0
       b:    c7 45 fc 00 00 00 00     mov DWORD PTR [rbp-0x4],0x0 #将0复制给[rbp-0x4] 这个内存地址,即 a = 0
      12:    8b 45 f8     mov eax,DWORD PTR [rbp-0x8] #将i的值1复制给寄存器eax
      15:    83 f8 01     cmp eax,0x1 #将eax与1进行比较,如果true则执行下面的je指令,跳转到21
      18:    74 07     je 21 <main+0x21>
      1a:    83 f8 02     cmp eax,0x2 #将eax与2进行比较,如果true则执行下面的je指令,跳转到2a
      1d:    74 0b     je 2a <main+0x2a>
      1f:    eb 12     jmp 33 <main+0x33> #跳转到33
      21:    c7 45 fc 01 00 00 00     mov DWORD PTR [rbp-0x4],0x1 #a = 1
      28:    eb 11     jmp 3b <main+0x3b>
      2a:    c7 45 fc 02 00 00 00     mov DWORD PTR [rbp-0x4],0x2 #a = 2
      31:    eb 08     jmp 3b <main+0x3b>
      33:    c7 45 fc 03 00 00 00     mov DWORD PTR [rbp-0x4],0x3 #a = 3
      3a:    90     nop
      3b:    b8 01 00 00 00     mov eax,0x1 # eax = 1
      40:    5d     pop rbp
      41:    c3      ret # 返回eax
    展开

    作者回复: 👍

    
     4
  • Linuxer
    2019-05-08
    51: b8 00 00 00 00 mov eax,0x0
    这个会不会是main的返回值呢?

    作者回复: Linuxer同学你说得对,这个就是main的返回值。

    
     4
  • Amark
    2019-06-15
    上一节讲的是: 高级语言的代码到汇编语言再到机器指令的过程,而这节其实是: 计算机如何执行机器指令的,分为顺序执行与跳转执行的方式,当然这两种执行方式是通过指令寄存器,PC寄存器,条形码寄存器实现的。
     1
     3
  • 免费的人
    2019-05-09
    switch case 要看编译器有没有生成跳表,没有的话跟if else效率应该是一样的,比如case个数比较少的情况

    作者回复: 👍

    
     3
  • 小肚腩era
    2019-05-08
    今年大四,正在实习。在实际工作慢慢发现自己基础知识的薄弱,所以现在也是抓紧时间在补习这些知识。听老师这一讲,又想起了汇编的知识,比起以前,又有了更深的理解。十分期待老师更新专栏~

    作者回复: 👍加油

    
     3
  • 大马猴
    2019-11-24
    [rbp-0x4] 则是一个寄存器的地址,这里讲错了,rbp寄存器保存的是栈基址
    
     2
  • 晴天~
    2019-08-19
    徐老师好,有一个问题想请教一下:
    执行cmp这样的指令会改变条件码寄存器部分状态码的值,我想请教一下,cpu的条件码寄存器肯定不止一个,后面的jne,jle需要判断条件码的值执行跳转,是不是有一个机制来保证jne这样的指令找到正确的条件码寄存器呢?

    作者回复: 晴天~同学

    你好,为什么不能只有一个呢?x86里一个CPU只有一个条件吗寄存器

    
     2
  • -W.LI-
    2019-05-26
    switch case 我猜是用jump if equal写的,所有判断顺序写一起,所有处理逻辑顺序写一起,满足条件就跳到对应的处理逻辑,遇见break就跳转到switch块的外面,如果没有就会顺序执行剩下的处理逻辑(case穿透)。

    作者回复: 可以写一些带switch...case的程序试一下,你会发现编译器是很聪明的

    
     2
  • hello
    2019-05-24
    老师,您上一讲讲道指令都是32位,为什么这次jne 指令是16位,mov指令是56位?

    作者回复: hello同学你好,

    上一讲里面,我们是拿MIPS这样比较简单的指令集来举例子的,里面的指令都是等长的。

    这一讲里面,实际是在一台Intel CPU的Linux机器上的程序,Intel的指令集里面的每条指令的长度并不是都相等的。

    
     2
  • 活的潇洒
    2019-05-16
    反复阅读、并实践操作、我用的是CentOS实践过程中遇到一些问题我都记录下来了,具体详见我的博客
    https://www.cnblogs.com/luoahong/p/10862085.html

    作者回复: 👍感谢分享给大家

     1
     2
  • aiter
    2019-05-12
    徐老师好~
    C语言我不会,。,努力看了半天,算是懂了大部分,但是for循环那里还是有点问题~汇编语言里,jmp 1e 之后,应该是做比较cmp,但是为什么不是0和3比较,而是和16进制的2(0x2)比较?
    ————-
    因为后面用的jle(jump if less or equal) <=2.如果是使用jl(jump if less) <3.应该是编译器的优化行为?可以自己写汇编代码,使用jl 0x3试试效果是否一样
    作者回复: aiter同学谢谢。我回复了,不过你这里的理解不太对,jle指令并不是和2做比较,而是判断标志位的,jle 和 jl 用的是不同的标志位,具体可以看看这个reference http://www.unixwiz.net/techtips/x86-jumps.html
    ———-
    谢谢老师,我再学习一下。我上面想说的是cmpl指令后面的数字,比如把代码改为:for(i =0;i<50;i++),objdump的汇编就是cmpl $0x31,-0x8(rbp). 0x31=49。但是这个cmpl比较指令,和后面的jle这个是什么关系(比如什么情况才会使用jl),再学习理解一下
    展开

    作者回复: cmp指令执行完成之后,会更新对应的标志位
    而jle指令读取特定的标志位来决定是否跳转
    使用什么指令是由编译器决定的
    不同的跳转指令有时候可以实现相同的功能
    不同的跳转指令读取的标志位可以参见
    http://marin.jb.free.fr/jumps/

    
     2
  • 二进制
    2019-05-10
    认真学一遍汇编课程,你会觉得这文章很简单。

    作者回复: 👍

    
     2
我们在线,来聊聊吧