汇编(四)
前言:
内存分区是编译器帮我们做的,属于硬件相关。
macho文件是由代码、数据、macho的描述信息等组成
进行下面的学习之前请先了解一下内存五大区
接下来让我们一起来探索全局变量、常量 、静态区的内存
一. 全局变量和常量
创建空工程Demo,为了防止ViewController中OC代码的干扰,我们在main.m文件中编写代码探讨
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int g = 12;
int func(int a,int b){
printf("haha");
int c = a + g;
return c;
}
int main(int argc, char * argv[]) {
func(1, 2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
image.png
继续执行上面汇编代码到这一步
image.png// func函数的汇编代码如下
Demo`func:
-> 0x104b82170 <+0>: sub sp, sp, #0x20 ; =0x20
0x104b82174 <+4>: stp x29, x30, [sp, #0x10]
0x104b82178 <+8>: add x29, sp, #0x10 ; =0x10
// 1 2 两个参数分别入栈
0x104b8217c <+12>: stur w0, [x29, #-0x4]
0x104b82180 <+16>: str w1, [sp, #0x8]
// x0寄存器中存的是地址,这个地址是4个字节,存的是 haha
// 下面这两条指令是怎么得到0x0000000104b83f8d内存地址呢?
// adrp 全称address page,按页寻找内存地址 这里是先将1这个值左移12位得到0x1000,再把pc寄存器0x104b82184低12位清0得到0x104b82000,两者再相加得到0x104b83000
// 0x104b83000 再添加0xf8d 得到0x0000000104b83f8d
0x104b82184 <+20>: adrp x0, 1
0x104b82188 <+24>: add x0, x0, #0xf8d ; =0xf8d
// 调用printf函数
0x104b8218c <+28>: bl 0x104b825ac ; symbol stub for: printf
// 这里拿到参数a 也就是 1
0x104b82190 <+32>: ldur w8, [x29, #-0x4]
// 这里通过偏移得到 0x104b89570,也就是参数g值为12
0x104b82194 <+36>: adrp x9, 7
0x104b82198 <+40>: add x9, x9, #0x570 ; =0x570
// 把x9 读到w10寄存器
0x104b8219c <+44>: ldr w10, [x9]
0x104b821a0 <+48>: add w8, w8, w10
0x104b821a4 <+52>: str w8, [sp, #0x4]
0x104b821a8 <+56>: ldr w8, [sp, #0x4]
// 把计算的结果给到x0寄存器,作为返回值
0x104b821ac <+60>: mov x0, x8
0x104b821b0 <+64>: ldp x29, x30, [sp, #0x10]
0x104b821b4 <+68>: add sp, sp, #0x20 ; =0x20
0x104b821b8 <+72>: ret
上面执行完 0x104b82184 <+20>: adrp x0, 1得到0x104b83000,fff是4095加上0就是4096 刚好对应4k,而一页大小是4096,所以这里的0x104b83000刚好是某一页的开始,adrp就意味着是先找到某一页的地址,再加上偏移量#0xf8d找到x0 haha的地址
// 终端执行PAGESIZE得到4096,说明一页大小是4096
hello-world$ PAGESIZE
4096
- 页号是编译器编译的时候确定的
- 0x104b82184 <+20>: adrp x0, 1的参照是pc寄存器0x104b82184,以当前这个代码段的地址作为参照
- 其中偏移值作为参照有两种方式, 第一种从文件起始位置偏移,第二种是当前代码地址偏移,这里是通过当前代码地址偏移进行计算
这里为什么不能直接加到字符串的位置?
- 因为0x104b82184地址中的前4位 104b是随机获取的,这里只能通过偏移值来找到字符串位置
这里我们做个练习加深对adrp指令的理解?0x1002bf888 <+20>: adrp x0, 2
- 这里0x是16进制,低12位这里的位指的是bit,也就是后面三个数清0
- 这里是先将2这个值左移12位得到0x2000
- 再把pc寄存器0x1002bf888低12位清0得到0x1002bf000
- 两者再相加得到0x1002c1000
小结
全局adrp与常量drp是一样的,都是通过基值加上偏移来获取。上面两处adrp没法区分是全局变量还是常量,但是可以拿到内存地址在macho中查看是全局变量还是常量。
二. 还原高级代码
下面我们通过反汇编来还原高级代码,这里需要借助工具Hopper,首先修改代码如下
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int g = 12;
int func(int a,int b){
printf("haha");
int c = a + g + b;
return c;
}
int main(int argc, char * argv[]) {
func(10, 20);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
编译Demo工程,选中Products目录下Demo.app -> Show in Finder -> 右键显示包内容 -> 找到可执行文件Demo -> 把Demo拖入Hopper
image.png image.png下面我们通过上面工具的汇编代码来还原高级代码
image.png image.png image.png ; ================ B E G I N N I N G O F P R O C E D U R E ================
_func:
// 函数的开始
0000000100006168 sub sp, sp, #0x20 ; CODE XREF=_main+32
000000010000616c stp x29, x30, [sp, #0x10]
0000000100006170 add x29, sp, #0x10
// 从这里可以看出函数为两个参数
0000000100006174 stur w0, [x29, #-0x4]
0000000100006178 str w1, [sp, #0x8]
// 还原出代码为 func(int a,int b)
func(int a,int b) { }
// 这一段代码调用的是 printf函数
000000010000617c adrp x0, #0x100007000 ; argument #1 for method imp___stubs__printf
// 工具直接定位出常量 haha
0000000100006180 add x0, x0, #0xf8d ; "haha"
0000000100006184 bl imp___stubs__printf
// 注意这里的0x100007000 是工具已经帮我们计算好了偏移量之后的值,执行完add 计算出内存地址 0000000100007f8d
// 根据Hopper工具,我们可以得出地址为0000000100007f8d 值为haha
// 还原出代码为printf("haha");
printf("haha");
0000000100006188 ldur w8, [x29, #-0x4]
// 还原出代码为 int w8 = a;
int w8 = a;
000000010000618c adrp x9, #0x10000d000
// 这里也定位出全局变量_g
0000000100006190 add x9, x9, #0x570 ; _g
// 根据Hopper工具,我们可以得出地址为000000010000d570 值为0x0c也就是12
// 还原出代码为 int g = 12; 注意⚠️ 因为是全局变量(使用烂苹果软件MachOView看出是在data区被改动,所以是全局变量),要写在func函数外面
g;
0000000100006194 ldr w10, x9
// 还原出代码为 int w10 = g;
int w10 = g;
0000000100006198 add w8, w8, w10
// 还原出代码为 w8 += w10;
w8 += w10;
000000010000619c ldr w10, [sp, #0x8]
// 还原出代码为 int w10 = b;
w10 = b;
00000001000061a0 add w8, w8, w10
// 还原出代码为 w8 += w10;
w8 += w10;
00000001000061a4 str w8, [sp, #0x4]
00000001000061a8 ldr w8, [sp, #0x4]
00000001000061ac mov x0, x8
// 还原出代码为 return w8;
return w8;
// 函数的结束
00000001000061b0 ldp x29, x30, [sp, #0x10]
00000001000061b4 add sp, sp, #0x20
00000001000061b8 ret
根据上面汇编代码还原简化为下面代码
int g = 12;
func(int a,int b) {
printf("haha");
int w8 = a;
int w10 = g;
w8 += w10;
w10 = b;
w8 += w10;
return w8;
}
// 接着简化代码如下
int g = 12;
func(int a,int b) {
printf("haha");
return b + g + a;
}
通过最终还原的代码与上面代码比对,发现汇编代码还原高级语言代码成功。注意⚠️ 还原代码必须从下往上还原,这就是逆向
三. if的识别
创建空工程002--if的识别,我们还在main.m文件中编写代码探讨
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int g = 12;
void func(int a,int b){
if (a > b) {
g = a;
}else{
g = b;
}
}
int main(int argc, char * argv[]) {
func(1, 2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
编译002--if的识别工程,找到可执行文件拖入Hopper,如果不是真机编译,会出现下图情况
image.png image.png接下来我们用真机编译,可执行文件拖入Hopper
image.png// func汇编代码如下
; ================ B E G I N N I N G O F P R O C E D U R E ================
_func:
0000000100006188 sub sp, sp, #0x10 ; CODE XREF=_main+32
000000010000618c str w0, [sp, #0xc]
0000000100006190 str w1, [sp, #0x8]
0000000100006194 ldr w8, [sp, #0xc]
0000000100006198 ldr w9, [sp, #0x8]
// 通过尾数判断func函数,无返回值
// 上面还原出代码为 void func(int a, int b) { int w8 = a; int w9 = b; }
void func(int a, int b) {
int w8 = a;
int w9 = b;
}
// 两个参数的比较,cmp执行的是减法 不影响目标寄存器w8 w9,只看减法的结果,修改标记寄存器
000000010000619c cmp w8, w9
// b.le 相当于 小于等于
00000001000061a0 b.le loc_1000061b8
// 还原出代码 if (w8 - w9) { }
if (w8 - w9) { // 小于等于
}
// 执行大于逻辑
00000001000061a4 ldr w8, [sp, #0xc]
00000001000061a8 adrp x9, #0x10000d000
00000001000061ac add x9, x9, #0x568 ; _g
00000001000061b0 str w8, x9
00000001000061b4 b loc_1000061c8
// 还原出代码 g = b;
g = b;
// 执行小于等于的逻辑
loc_1000061b8:
00000001000061b8 ldr w8, [sp, #0x8] ; CODE XREF=_func+24
00000001000061bc adrp x9, #0x10000d000
00000001000061c0 add x9, x9, #0x568 ; _g
// 根据Hopper工具,我们可以得出地址为000000010000d568 值为0x0c也就是12,还原出代码为 int g = 12;
// 把w8 写入 x9,也就是写入g中
// 这里只能看出w8是4个字节,因为是低32位,这里有可能是Int 也有可能是uInt
00000001000061c4 str w8, x9
// 还原出代码 g = w8;
g = w8;
loc_1000061c8:
00000001000061c8 add sp, sp, #0x10 ; CODE XREF=_func+44
00000001000061cc ret
; endp
// 最终通过汇编代码还原出的高级代码如下
int g = 12;
void func2(int a,int b){
if (a > b) {
g = a;
}else{
g = b;
}
}
小结
cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.LT 标号:比较结果是小于,执行标号,否则不跳转
- B.LE(less equal) 标号:比较结果是小于等于,执行标号,否则不跳转
- B.NE(not equal) 标号:比较结果是不等于,执行标号,否则不跳转
注意⚠️ cmp指令下面执行的是 else 条件
四. 循环
创建空工程003--Loop,我们还在main.m文件中编写代码探讨
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
int nSum = 0;
int i = 0;
do {
nSum = nSum + 1;
i++;
} while (i < 100);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
真机编译,可执行文件拖入Hopper,获取汇编代码
// main汇编代码如下
_main:
00000001000061b8 sub sp, sp, #0x40
00000001000061bc stp x29, x30, [sp, #0x30]
00000001000061c0 add x29, sp, #0x30
00000001000061c4 stur wzr, [x29, #-0x4]
00000001000061c8 stur w0, [x29, #-0x8]
00000001000061cc stur x1, [x29, #-0x10]
00000001000061d0 stur wzr, [x29, #-0x14]
// 把0放入一个内存地址中
00000001000061d4 str wzr, [sp, #0x18]
loc_1000061d8:
// 这里是do的操作
00000001000061d8 ldur w8, [x29, #-0x14] ; CODE XREF=_main+64
00000001000061dc add w8, w8, #0x1
00000001000061e0 stur w8, [x29, #-0x14]
// 读出来给到w8寄存器
00000001000061e4 ldr w8, [sp, #0x18]
// w8寄存器加1
00000001000061e8 add w8, w8, #0x1
00000001000061ec str w8, [sp, #0x18]
00000001000061f0 ldr w8, [sp, #0x18]
// 比较指令这里是while条件,0x64就是100,和100比较,小于就跳回来
00000001000061f4 cmp w8, #0x64
00000001000061f8 b.lt loc_1000061d8
00000001000061fc ldur w0, [x29, #-0x8]
0000000100006200 ldur x1, [x29, #-0x10]
0000000100006204 adrp x8, #0x10000d000
0000000100006208 add x8, x8, #0x398 ; objc_cls_ref_AppDelegate
000000010000620c ldr x8, x8
0000000100006210 str w0, [sp, #0x14]
0000000100006214 mov x0, x8
0000000100006218 str x1, [sp, #0x8]
000000010000621c bl imp___stubs__objc_opt_class
0000000100006220 bl imp___stubs__NSStringFromClass
0000000100006224 mov x29, x29
0000000100006228 bl imp___stubs__objc_retainAutoreleasedReturnValue
000000010000622c ldr w9, [sp, #0x14]
0000000100006230 str x0, sp
0000000100006234 mov x0, x9
0000000100006238 ldr x1, [sp, #0x8]
000000010000623c movz x8, #0x0
0000000100006240 mov x2, x8
0000000100006244 ldr x3, sp
0000000100006248 bl imp___stubs__UIApplicationMain
000000010000624c stur w0, [x29, #-0x4]
0000000100006250 ldr x0, sp
0000000100006254 bl imp___stubs__objc_release
0000000100006258 ldur w0, [x29, #-0x4]
000000010000625c ldp x29, x30, [sp, #0x30]
0000000100006260 add sp, sp, #0x40
0000000100006264 ret
; endp
修改main.m代码如下
int main(int argc, char * argv[]) {
int nSum = 0;
int i = 0;
while (i < 100) {
nSum = nSum + 1;
i++;
} ;
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 核心汇编代码如下
loc_1000061d4:
00000001000061d4 ldr w8, [sp, #0x18] ; CODE XREF=_main+68
00000001000061d8 cmp w8, #0x64
// 不满足条件跳转执行loc_1000061fc
00000001000061dc b.ge loc_1000061fc
00000001000061e0 ldur w8, [x29, #-0x14]
00000001000061e4 add w8, w8, #0x1
00000001000061e8 stur w8, [x29, #-0x14]
00000001000061ec ldr w8, [sp, #0x18]
00000001000061f0 add w8, w8, #0x1
00000001000061f4 str w8, [sp, #0x18]
00000001000061f8 b loc_1000061d4
loc_1000061fc:
00000001000061fc ldur w0, [x29, #-0x8] ; CODE XREF=_main+40
0000000100006200 ldur x1, [x29, #-0x10]
0000000100006204 adrp x8, #0x10000d000
0000000100006208 add x8, x8, #0x398 ; objc_cls_ref_AppDelegate
000000010000620c ldr x8, x8
0000000100006210 str w0, [sp, #0x14]
0000000100006214 mov x0, x8
0000000100006218 str x1, [sp, #0x8]
000000010000621c bl imp___stubs__objc_opt_class
0000000100006220 bl imp___stubs__NSStringFromClass
0000000100006224 mov x29, x29
...
再次修改main.m代码如下
int main(int argc, char * argv[]) {
int nSum = 0;
for (int i = 0; i < 100; i++) {
nSum = nSum + 1;
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 核心汇编代码如下
loc_1000061d4:
00000001000061d4 ldr w8, [sp, #0x14] ; CODE XREF=_main+72
00000001000061d8 cmp w8, #0x64
00000001000061dc b.ge loc_1000061fc
00000001000061e0 ldur w8, [x29, #-0x14]
00000001000061e4 add w8, w8, #0x1
00000001000061e8 stur w8, [x29, #-0x14]
00000001000061ec ldr w8, [sp, #0x14]
00000001000061f0 add w8, w8, #0x1
00000001000061f4 str w8, [sp, #0x14]
00000001000061f8 b loc_1000061d4
loc_1000061fc:
00000001000061fc ldur w0, [x29, #-0x8] ; CODE XREF=_main+44
0000000100006200 ldur x1, [x29, #-0x10]
0000000100006204 adrp x8, #0x10000d000
0000000100006208 add x8, x8, #0x398 ; objc_cls_ref_AppDelegate
000000010000620c ldr x8, x8
0000000100006210 str w0, [sp, #0x10]
...
// 可以看出来跟while 汇编代码一摸一样,所以两者可以代替
五. 选择(上)
创建空工程004--选择,我们还在main.m文件中编写代码探讨
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void funcA(int a){
switch (a) {//
case 1:
printf("打坐");
break;
case 2:
printf("加红");
break;
case 3:
printf("加蓝");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(1);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// funcA汇编代码如下
004--选择`funcA:
0x100962114 <+0>: sub sp, sp, #0x20 ; =0x20
0x100962118 <+4>: stp x29, x30, [sp, #0x10]
0x10096211c <+8>: add x29, sp, #0x10 ; =0x10
// wo 寄存器保存一下,然后再取出来
0x100962120 <+12>: stur w0, [x29, #-0x4]
-> 0x100962124 <+16>: ldur w8, [x29, #-0x4]
// 比较,相等就跳转
0x100962128 <+20>: cmp w8, #0x1 ; =0x1
0x10096212c <+24>: str w8, [sp, #0x8]
0x100962130 <+28>: b.eq 0x100962158 ; <+68> at main.m
0x100962134 <+32>: b 0x100962138 ; <+36> at main.m
0x100962138 <+36>: ldr w8, [sp, #0x8]
// 比较,相等就跳转
0x10096213c <+40>: cmp w8, #0x2 ; =0x2
0x100962140 <+44>: b.eq 0x100962168 ; <+84> at main.m
0x100962144 <+48>: b 0x100962148 ; <+52> at main.m
0x100962148 <+52>: ldr w8, [sp, #0x8]
// 比较,相等就跳转
0x10096214c <+56>: cmp w8, #0x3 ; =0x3
0x100962150 <+60>: b.eq 0x100962178 ; <+100> at main.m
0x100962154 <+64>: b 0x100962188 ; <+116> at main.m
// 拿出常量打印
0x100962158 <+68>: adrp x0, 1
0x10096215c <+72>: add x0, x0, #0xf6d ; =0xf6d
0x100962160 <+76>: bl 0x10096258c ; symbol stub for: printf
0x100962164 <+80>: b 0x100962194 ; <+128> at main.m:30:1
0x100962168 <+84>: adrp x0, 1
0x10096216c <+88>: add x0, x0, #0xf74 ; =0xf74
0x100962170 <+92>: bl 0x10096258c ; symbol stub for: printf
0x100962174 <+96>: b 0x100962194 ; <+128> at main.m:30:1
0x100962178 <+100>: adrp x0, 1
0x10096217c <+104>: add x0, x0, #0xf7b ; =0xf7b
0x100962180 <+108>: bl 0x10096258c ; symbol stub for: printf
0x100962184 <+112>: b 0x100962194 ; <+128> at main.m:30:1
0x100962188 <+116>: adrp x0, 1
0x10096218c <+120>: add x0, x0, #0xf82 ; =0xf82
0x100962190 <+124>: bl 0x10096258c ; symbol stub for: printf
0x100962194 <+128>: ldp x29, x30, [sp, #0x10]
0x100962198 <+132>: add sp, sp, #0x20 ; =0x20
0x10096219c <+136>: ret
// 可以发现上面汇编代码与if 汇编是一样的
修改main.m代码如下
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;
}
}
// funcA汇编代码如下
004--选择`funcA:
0x1024660f0 <+0>: sub sp, sp, #0x20 ; =0x20
0x1024660f4 <+4>: stp x29, x30, [sp, #0x10]
0x1024660f8 <+8>: add x29, sp, #0x10 ; =0x10
// 参数入栈
0x1024660fc <+12>: stur w0, [x29, #-0x4]
-> 0x102466100 <+16>: ldur w8, [x29, #-0x4]
// 这里没有了类似if的汇编,也没有了很多个cmp
// subs 让参数减1,这里会影响状态寄存器减1
0x102466104 <+20>: subs w8, w8, #0x1 ; =0x1
0x102466108 <+24>: mov x9, x8
0x10246610c <+28>: ubfx x9, x9, #0, #32
// 判断是否等于3
0x102466110 <+32>: cmp x9, #0x3 ; =0x3
0x102466114 <+36>: str x9, [sp]
// 跳转
0x102466118 <+40>: b.hi 0x102466174 ; <+132> at main.m
0x10246611c <+44>: adrp x8, 0
0x102466120 <+48>: add x8, x8, #0x18c ; =0x18c
0x102466124 <+52>: ldr x11, [sp]
0x102466128 <+56>: ldrsw x10, [x8, x11, lsl #2]
0x10246612c <+60>: add x9, x8, x10
0x102466130 <+64>: br x9
0x102466134 <+68>: adrp x0, 1
0x102466138 <+72>: add x0, x0, #0xf69 ; =0xf69
0x10246613c <+76>: bl 0x102466588 ; symbol stub for: printf
0x102466140 <+80>: b 0x102466180 ; <+144> at main.m:30:1
0x102466144 <+84>: adrp x0, 1
0x102466148 <+88>: add x0, x0, #0xf70 ; =0xf70
0x10246614c <+92>: bl 0x102466588 ; symbol stub for: printf
0x102466150 <+96>: b 0x102466180 ; <+144> at main.m:30:1
0x102466154 <+100>: adrp x0, 1
0x102466158 <+104>: add x0, x0, #0xf77 ; =0xf77
0x10246615c <+108>: bl 0x102466588 ; symbol stub for: printf
0x102466160 <+112>: b 0x102466180 ; <+144> at main.m:30:1
0x102466164 <+116>: adrp x0, 1
0x102466168 <+120>: add x0, x0, #0xf7e ; =0xf7e
0x10246616c <+124>: bl 0x102466588 ; symbol stub for: printf
0x102466170 <+128>: b 0x102466180 ; <+144> at main.m:30:1
0x102466174 <+132>: adrp x0, 1
0x102466178 <+136>: add x0, x0, #0xf85 ; =0xf85
0x10246617c <+140>: bl 0x102466588 ; symbol stub for: printf
0x102466180 <+144>: ldp x29, x30, [sp, #0x10]
0x102466184 <+148>: add sp, sp, #0x20 ; =0x20
0x102466188 <+152>: ret
小结
- switch 如果条件语句小于等于3个,汇编代码就是if else 的形式。
- 当参数超过3个,就会建立一张连续的数据表,把每一条判断语句做的事情放入表中相应地址,每8个字节放一个地址 ,通过一次运算得到一个地址值,然后执行对应代码,不需要多次if else 判断,这就是底层的优化
- 这里上面是减1和3比较得到一个index值,通过index值从数据表中找内存地址,找到之后直接执行对应的逻辑
注意⚠️ switch 判断条件比较多的话,一直if else 会浪费时间,由于上面都是连续的数字,这个时候就会创建表,投入空间来换时间。如果上面判断条件不是连续的,底层就不会创建表来进行优化,又会执行if else 的判断。
// 下面这种形式,虽然条件语句是4个,但判断条件不是连续的,系统底层不会创建表来进行优化
void funcA(int a){
switch (a) {
case 1:
printf("打坐");
break;
case 200:
printf("加红");
break;
case 35:
printf("加蓝");
break;
case 41:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
- 连续的表,系统底层会找规律来创建表进行优化
- 如果判断逻辑是连续的并且超过了3个,尽可能使用switch语句,因为底层会进行优化
上面是怎么生成数据表?又是怎么找到对应的地址值?限于篇幅,我们之后继续探讨......