1、if 语句源代码
#include<iostream>
int main() {
int no = 4;
if (no == 1) {
printf("no is 1");
} else if (no == 2){
printf("no is 2");
} else if (no == 3){
printf("no is 3");
} else if (no == 4) {
printf("no is 4");
} else if (no == 5) {
printf("no is 5");
} else {
printf("no is other");
}
int age = 4;
}
2、对以上代码进行反汇编,得到汇编代码
// 把4赋值 int no = 4;
00007FF75D0B191A mov dword ptr[no], 4
//cmp == compare 比较 (把4和1比较 if (no == 1))
00007FF75D0B1921 cmp dword ptr[no], 1
// jne == jump not equal 不相等的时候就会跳转,跳转到 07FF75D0B1935h 这个地址 ,就是下面这一句代码: 00007FF75D0B1935 cmp dword ptr[no], 2
00007FF75D0B1925 jne main + 45h(07FF75D0B1935h)
// 如果相等,继续往下执行,输入 no is 1
00007FF75D0B1927 lea rcx, [string "no is 1" (07FF75D0B9C28h)]
//call 调用代码,也就是调用printf函数
00007FF75D0B192E call printf(07FF75D0B11D6h)
//jmp 无条件跳转 ,这里是跳转到 07FF75D0B1991h 这个地址 ,也就是执行: mov dword ptr[age], 4 (源代码是:int age = 4)这时候已经跳出if - else 语句了
00007FF75D0B1933 jmp main + 0A1h(07FF75D0B1991h)
/************************ 第二个if语句判断,跟上面相同的逻辑 ******************************/
//cmp == compare 比较 (把4和1比较 if (no == 1))
00007FF75D0B1935 cmp dword ptr[no], 2
00007FF75D0B1939 jne main + 59h(07FF75D0B1949h)
00007FF75D0B193B lea rcx, [string "no is 2" (07FF75D0B9C38h)]
00007FF75D0B1942 call printf(07FF75D0B11D6h)
00007FF75D0B1947 jmp main + 0A1h(07FF75D0B1991h)
/************************ 第三个if语句判断,跟上面相同的逻辑 ******************************/
00007FF75D0B1949 cmp dword ptr[no], 3
00007FF75D0B194D jne main + 6Dh(07FF75D0B195Dh)
00007FF75D0B194F lea rcx, [string "no is 3" (07FF75D0B9C48h)]
00007FF75D0B1956 call printf(07FF75D0B11D6h)
00007FF75D0B195B jmp main + 0A1h(07FF75D0B1991h)
/************************ 第四个if语句判断,跟上面相同的逻辑 ******************************/
00007FF75D0B195D cmp dword ptr[no], 4
00007FF75D0B1961 jne main + 81h(07FF75D0B1971h)
00007FF75D0B1963 lea rcx, [string "no is 4" (07FF75D0B9C58h)]
00007FF75D0B196A call printf(07FF75D0B11D6h)
00007FF75D0B196F jmp main + 0A1h(07FF75D0B1991h)
/************************ 第五个if语句判断,跟上面相同的逻辑 ******************************/
00007FF75D0B1971 cmp dword ptr[no], 5
00007FF75D0B1975 jne main + 95h(07FF75D0B1985h)
00007FF75D0B1977 lea rcx, [string "no is 5" (07FF75D0B9C68h)]
00007FF75D0B197E call printf(07FF75D0B11D6h)
00007FF75D0B1983 jmp main + 0A1h(07FF75D0B1991h)
/************************ 最后一个if语句判断,跟上面相同的逻辑 ******************************/
00007FF75D0B1985 lea rcx, [string "no is other" (07FF75D0B9C78h)]
00007FF75D0B198C call printf(07FF75D0B11D6h)
/************************************* int age = 4, if语句结束的提示代码 **************************************/
00007FF75D0B1991 mov dword ptr[age], 4
3、if语句的使用总结:
从上面的代码可以看出:如果用if语句,会把每一个条件都走一遍,知道匹配到对应的条件位置为止,所以写if-else语句的时候,尽量把出现的概率比较大的匹配值写在前面,例如 i== 4 或者 i == 3 会经常出现,那么代码应该这么写,效率会更高
if (no == 3) {
printf("no is 3");
} else if (no == 4) {
printf("no is 4");
} else if (no == 1) {
printf("no is 3");
} else if (no == 2) {
printf("no is 4");
} else if (no == 5) {
printf("no is 5");
} else {
printf("no is other");
}
4、switch语句的底层实现
源码:
#include<iostream>
int main() {
int no = 4;
switch (no)
{
case 0:
printf("i is 0");
case 1:
printf("i is 1");
case 2:
printf("i is 2");
case 3:
printf("i is 3");
case 4:
printf("i is 4");
case 5:
printf("i is 5");
default:
printf("i is other");
break;
}
int age = 4;
}
5、switch 反汇编分析:
switch的原理:
通过hash 算法,得到一个hash之后的地址值,跟if不一样的是不需要一个一个条件去比较,而是直接算出对应的值的内存地址,然后跳转到对应的内存地址,直接执行对应的代码,这是一种典型的空间换时间的方式,
// 把4赋值 int no = 4;
00007FF6F462191A mov dword ptr[no], 4
00007FF6F4621921 mov eax, dword ptr[no]
// hash 算法,得到一个hash之后的地址值,跟if不一样的是不需要一个一个比较,而是直接算出对应的值的内存地址,然后跳转到对应的内存地址,直接执行对应的代码
// 最终算出来的内存地址放在eax里面: 00007FF6F4621941 mov eax, dword ptr[rcx + rax * 4 + 119B4h],
// 然后jmp eax,无条件跳转到eax这个地址,执行对应的代码,也就是case语句
00007FF6F4621924 mov dword ptr[rbp + 0F4h], eax
00007FF6F462192A cmp dword ptr[rbp + 0F4h], 5
00007FF6F4621931 ja $LN9 + 0Ch(07FF6F4621995h)
00007FF6F4621933 movsxd rax, dword ptr[rbp + 0F4h]
00007FF6F462193A lea rcx, [7FF6F4610000h] // 目标地址传送指令
00007FF6F4621941 mov eax, dword ptr[rcx + rax * 4 + 119B4h]
00007FF6F4621948 add rax, rcx
00007FF6F462194B jmp rax
// case 0 里面的代码
$LN4 :
00007FF6F462194D lea rcx, [string "i is 0" (07FF6F4629C10h)]
00007FF6F4621954 call printf(07FF6F46211D6h)
// case 1 里面的代码
$LN5 :
00007FF6F4621959 lea rcx, [string "i is 1" (07FF6F4629C18h)]
00007FF6F4621960 call printf(07FF6F46211D6h)
// case 2 里面的代码
$LN6 :
00007FF6F4621965 lea rcx, [string "i is 2" (07FF6F4629C20h)]
00007FF6F462196C call printf(07FF6F46211D6h)
// case 3 里面的代码
$LN7 :
00007FF6F4621971 lea rcx, [string "i is 3" (07FF6F4629C30h)]
00007FF6F4621978 call printf(07FF6F46211D6h)
// case 4 里面的代码
$LN8 :
00007FF6F462197D lea rcx, [string "i is 4" (07FF6F4629C40h)]
00007FF6F4621984 call printf(07FF6F46211D6h)
// case 5 里面的代码
$LN9 :
00007FF6F4621989 lea rcx, [string "i is 5" (07FF6F4629C50h)]
00007FF6F4621990 call printf(07FF6F46211D6h)
// default 里面的代码
00007FF6F4621995 lea rcx, [string "i is other" (07FF6F4629D48h)]
00007FF6F462199C call printf(07FF6F46211D6h)
// int age = 4
00007FF6F46219A1 mov dword ptr[age], 4
6、最后的总结:
(1)用能用switch的,尽量不要用if ,如果用了if,就要结合else if ,并且尽量把条件出现的概率比较高的,放在最前面
(2)不要小看这点点的性能优化,代码的性能优化其实更多的是一个习惯问题,一旦养成了这个习惯,代码多了,积累起来,这一点点的性能影响就很大了
(3)switch不用全部判断是因为把switch里面的判断一个做了hash计算,可以直接得到地址值,然后跳到执行对应的代码,但其实也不是所有的switch都会通过特定算法生成值,如果条件值比较少,就不会生成hash计算。而是直接比较
(4)苹果官方为什么说swift性能比oc要好,swift对于switch使用就是其中一个很好地体现,swift的switch支持范围值,填补了oc里面的这个功能的空虚,例如以下swift代码:
let no = 9
switch no{
case 0..9:
print("0-9")
case 10..20:
print("10-20")
default:
print("other")
}