循环&选择
cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。 一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.LT 标号:比价结果是小于(less than),执行标号,否则不跳转
- B.LE 标号:比较结果是小于等于(less than or qeual to),执行标号,否则不跳转
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于(equal to),执行标号,否则不跳转
- B.NE 标号:比较结果是不等于(not equal to),执行标号,否则不跳转
- B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
- B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
void func(int a,int b){
if (a > b) {
g = a;
}else{
g = b;
}
}
- if的比较汇编
- CMP指令比较
- CMP其实是做减法,然后不影响目标寄存器,只影响标记寄存器
- for
- do while 循环: 判断条件在后面,满足条件往外跳
- for 循环 和 while 循环很像,判断条件在里面,不满足就往外跳。 ##Switch 1、假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。 2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。 3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)。
- switch
void funcA(int a){
switch (a) {//
case 1:
printf("打坐");
break;
case 2:
printf("加红");
break;
case 3:
printf("加蓝");
break;
case 4:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
- 汇编解析
004--选择`funcA:
0x102b6e0f0 <+0>: sub sp, sp, #0x20 ; =0x20 //开辟栈空间
0x102b6e0f4 <+4>: stp x29, x30, [sp, #0x10] //X29 X30寄存器入栈
0x102b6e0f8 <+8>: add x29, sp, #0x10 ; =0x10 //x29 指向栈底
0x102b6e0fc <+12>: stur w0, [x29, #-0x4] //w0 参数入栈
0x102b6e100 <+16>: ldur w8, [x29, #-0x4] //w8 取出值 即 w0 中的值
0x102b6e104 <+20>: subs w8, w8, #0x1 ; =0x1 // w8 - 1 '1'是最小的case
0x102b6e108 <+24>: mov x9, x8 //x8 的值-> x9
0x102b6e10c <+28>: ubfx x9, x9, #0, #32 //将x9的高32位清0 将#0--#32位 清零 保留 后面位的值
0x102b6e110 <+32>: cmp x9, #0x3 ; =0x3 //x9与3作比较 3 是最小case 和 最大 case 的差
0x102b6e114 <+36>: str x9, [sp] //x9入栈保存
0x102b6e118 <+40>: b.hi 0x102b6e174 ; <+132> at main.m //hi无符号 大于 跳转到 0x102b6e174 否则继续往下走 说明case在区间之内
0x102b6e11c <+44>: adrp x8, 0
0x102b6e120 <+48>: add x8, x8, #0x18c ; =0x18c //x8 = 0x102b6e18c x8->一个存放了 最大case-最小case+defualt个数的负数的表 用于地址偏移
0x102b6e124 <+52>: ldr x11, [sp] //x9->x11
0x102b6e128 <+56>: ldrsw x10, [x8, x11, lsl #2] //x11左移2位 1->4 再与 x8 相加 赋值给 x10
0x102b6e12c <+60>: add x9, x8, x10
0x102b6e130 <+64>: br x9
0x102b6e134 <+68>: adrp x0, 1
0x102b6e138 <+72>: add x0, x0, #0xf69 ; =0xf69
0x102b6e13c <+76>: bl 0x102b6e588 ; symbol stub for: printf
0x102b6e140 <+80>: b 0x102b6e180 ; <+144> at main.m:30:1
0x102b6e144 <+84>: adrp x0, 1
// - 将PC寄存器的低12位清零
// - 将1的值,左移12位! 16进制就是0x1000
// - 以上两个结果相加放入x0寄存器
// 0x102b6e144->0x102b6e000-> '+1' -> 0x102b6f000 -> 放入到寄存器x0
0x102b6e148 <+88>: add x0, x0, #0xf70 ; =0xf70 //x0 + 0xf70 -> 0x102b6ff70 -> 放入到寄存器x0
0x102b6e14c <+92>: bl 0x102b6e588 ; symbol stub for: printf
0x102b6e150 <+96>: b 0x102b6e180 ; <+144> at main.m:30:1
0x102b6e154 <+100>: adrp x0, 1
0x102b6e158 <+104>: add x0, x0, #0xf77 ; =0xf77
0x102b6e15c <+108>: bl 0x102b6e588 ; symbol stub for: printf
0x102b6e160 <+112>: b 0x102b6e180 ; <+144> at main.m:30:1
0x102b6e164 <+116>: adrp x0, 1
0x102b6e168 <+120>: add x0, x0, #0xf7e ; =0xf7e
0x102b6e16c <+124>: bl 0x102b6e588 ; symbol stub for: printf
0x102b6e170 <+128>: b 0x102b6e180 ; <+144> at main.m:30:1
0x102b6e174 <+132>: adrp x0, 1
0x102b6e178 <+136>: add x0, x0, #0xf85 ; =0xf85
0x102b6e17c <+140>: bl 0x102b6e588 ; symbol stub for: printf
0x102b6e180 <+144>: ldp x29, x30, [sp, #0x10]
0x102b6e184 <+148>: add sp, sp, #0x20 ; =0x20
0x102b6e188 <+152>: ret
选择&指针
- 编译器优化
- 编译器的优化,LLVM优化过程,优化的是我们的指令多少
- 无用代码(去掉之后对执行结果没有影响)会被优化掉
- Swift
- 分支少于3个(switch没有啥意义),底层代码和if-else一致
- 分支段多,根据情况,编译器会生成一个表进行跳转,利用空间换取事件,效率非常高
- 各个分支常量差值较大的时候,编译器会在效率和内存中进行取舍
- 指针
- 指针的宽度:指针的宽度是指,它所指向的数据类型的宽度!
- 指针的自增自减是按指向的数据类型(指针的宽度)来运算的。