美文网首页
4、全局变量 & 循环选择和判断

4、全局变量 & 循环选择和判断

作者: Jax_YD | 来源:发表于2021-03-29 14:55 被阅读0次

    首先我们先来简单的回忆一下内存分区:

    • 代码区:存放代码,可读,可执行。

    • 栈区:参数、局部变量、临时数据。

    • 堆区:动态申请,可读、可写。

    • 全局区:全局变量,可读、可写。

    • 常量区:只读。

    • 常量区 & 全局区
      首先我们来看下面的代码:

      WX20210329-103103.png
      test对应的汇编代码如下:
      WX20210329-103447.png
    • 通过上图,我们得知,在我们汇编代码的断点位置:
      ix0寄存器里面存放的是,常量Aaron的地址值。
      ii地址值里面存放的是ASCII码。
      那么问题来了:这个地址值是怎么来的?
      其实通过逻辑推理不难判断,此时x0里面的值是通过下面这两条指令获得的:

    0x1021ea0f8 <+16>: adrp   x0, 1
    0x1021ea0fc <+20>: add    x0, x0, #0xf85            ; =0xf85 
    

    下面我们来解释一下这两条指令:

    0x1021ea0f8 <+16>: adrp   x0, 1
    这句指令的意思是:
    ①:将`1`左移`12位`, 得到的结果是:`1000`(注意一个字节是4位)
    ②:将`pc`寄存器里面的值的后12位清零,然后加上上面的结果,得到的结果是:`0x1021eb000`
    此时`x0`的值是`0x1021eb000`
    
    0x1040f60fc <+20>: add    x0, x0, #0xf85
    这句指令的意思是:
    ①:将`x0`与`0xf85`相加
    ②:将相加的结果再存入`x0`
    最终`x0`的值是`0x1021ebf85`
    

    这样就得到了我们的字符串常量Aaron
    其实我们的全局变量g也是这样获得的:

    WX20210329-105213.png

    讲到这里,可能就有人要问了,adrp指令里面的1/7这些又是什么意思呢?其实1就代表4K,怎么来的呢?
    首先我们说了:
    adrp1要左移12位,就是1000
    那么这3个0换成F就是FFF,正好是40950~40954096个数,正好是4K

    • tips:
      MacOS(M1除外)中,内存的一页是4KiOS中一页是16K。这个我们也可以通过终端指令来看一下:

    循环选择和判断

    首先我们先来认识一个新的指令:cmp(Compare)比较指令

    • CMP一个寄存器的内存另一个寄存器的内存立即数进行比较。但是不存储结果,只是正确的更改标志。
      其实就是进行减法,但是不影响当前寄存器内容,只是修改标记寄存器
      一般CMP做完判断后会进行跳转,后面通常会跟上B指令⚠️ 。
    • bl标号:跳转到标号处执行。
    • b.gt标号:比较结果是大于(greater than),执行标号,否则不跳转。
    • b.ge标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转。
    • b.eq标号:比较结果是等于,执行标号,否则不跳转。
    • b.hi标号:比较结果是无符号大于,执行标号,否则不跳转。
      接下来我们借助另一个工具Hopper来查看汇编代码,为反汇编做准备。

    • if else
      有这么一段代码,编译之后将可执行文件拖入Hopper中查看(注意这里我们是用真机编译的,ARM64)

    w8 & w9存储的就是我们的a & b,这里就不不做过多的分析。
    注意:我们代码中写的是if(a >b)cmp做减法的结果如果是非正数就跳转至:loc_100006100;如果是正数,就接着往下执行。
    • 如果是非正数,则loc_100006100代码块执行完毕之后,会直接进入loc_100006108代码块。()
    • 如果是正数,则继续执行接下来的代码块,然后通过b指令,跳转到loc_100006108代码块。

    这就是if else指令的汇编体现。同时我们又认识了一个新的指令:b.le(小于等于)。
    再认识一个:b.lt(小于)


    • loop(循环)
    • 首先我们来看一下doWhile


      可以看到,doWhile的汇编指令,先执行do,然后根据b.lt指令进行跳转或者继续执行。
    • while
    • for (int i = 0; i < 100; i++)

    可以发现,普通的for循环的汇编指令跟while没有什么区别。

    注意 ⚠️ :for in的汇编代码就要复杂很多,有兴趣的同学可以自己编译查看一下。


    • Swith
      ⚠️ 注意:此时我们只写了3个判断,并且还是连续的


      可以看到,当判断条件在3个及以内的时候,switch的汇编实现就是if else
      我们再来添加一个case:
    case 4:
       printf("1");
       break;
    

    我们会发下,当判断条件大于3个的时候,swith的汇编代码就变了,不再是之前的if else。这就是swith底层优化,如果判断条件大于3个的时候,就会创建一个表,表里面存储了每一个case对应的要执行的代码的地址。只要去查表就可以了。(当然,前提条件是,判断条件是连续的有规律的(或者差值不是很大的)。)

    下面我们看一下无序的switch:

    switch (a) {
            case 0:
                printf("1");
                break;
            case 10:
                printf("1");
                break;
            case 2:
                printf("1");
                break;
            case 40:
                printf("1");
                break;
            default:
                break;
        }
    

    会发现,此时switch又变成了if else

    我们在使用switch的时候,要注意case的数量,以及case的规律,这样才能提高代码的质量。

    这里我们只是简单的探讨了一下switch,后续我们会针对switch做一下更深入的研究。

    相关文章

      网友评论

          本文标题:4、全局变量 & 循环选择和判断

          本文链接:https://www.haomeiwen.com/subject/dxivhltx.html