条件执行和循环的本质

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41594698/article/details/97121563

来源:Java编程的逻辑

条件执行的本质

程序最终都是一条条的指令,CPU有一个指令指示器,指向下一条要执行的指令,CPU根据指示器的指示加载指令并且执行。指令大部分是具体的操作和运算,在执行这些操作时,执行完一个操作后,指令指示器会自动指向挨着的下一个指令。

但有一些特殊的指令,称为跳转指令,这些指令会修改指令指示器的值,让CPU跳到一个指定的地方执行。跳转有两种,一种是条件跳转,另一种是无条件跳转。条件跳转检查某个条件,满足则进行跳转,无条件跳转则是直接进行跳转。

if, else实际上会转换为这些跳转指令,比如说下面的代码:

1 int a=10;
2 if(a%2==0)
3 {
4    System.out.println("偶数");
5 }
6 //其他代码

转换到的转移指令可能是:

1 int a=10;
2 条件跳转: 如果a%2==0,跳转到第43 无条件跳转:跳转到第74 {
5    System.out.println("偶数");
6 }
7 //其他代码

你可能会奇怪其中的无条件跳转指令,没有它不行吗?不行,没有这条指令,不管什么条件,括号中的代码都会执行。

不过,对应的跳转指令也可能是:

1 int a=10;
2 条件跳转: 如果a%2!=0,跳转到第63 {
4    System.out.println("偶数");
5 }
6 //其他代码

这个就没有无条件跳转指令,具体怎么对应和编译器实现有关。在单一if的情况下可能不用无条件跳转指令,但稍微复杂一些的情况都需要。if, if/else, if/else if/else, 三元运算符都会转换为条件跳转和无条件跳转。但switch不太一样。

switch的转换和具体系统实现有关,如果分支比较少,可能会转换为跳转指令。但如果分支比较多,使用条件跳转会进行很多次的比较运算,效率比较低,可能会使用一种更为高效的方式,叫跳转表。跳转表是一个映射表,存储了可能的值以及要跳转到的地址,形如:

值1 代码块1的地址
值2 代码块2的地址
值n 代码块n的地址

跳转表为什么会更为高效呢?因为,其中的值必须为整数,且按大小顺序排序。按大小排序的整数可以使用高效的二分查找,即先与中间的值比,如果小于中间的值则在开始和中间值之间找,否则在中间值和末尾值之间找,每找一次缩小一倍查找范围。如果值是连续的,则跳转表还会进行特殊优化,优化为一个数组,连找都不用找了,值就是数组的下标索引,直接根据值就可以找到跳转的地址。即使值不是连续的,但数字比较密集,差的不多,编译器也可能会优化为一个数组型的跳转表,没有的值指向default分支。

程序源代码中的case值排列不要求是排序的,编译器会自动排序。之前说switch值的类型可以是byte, short, int, char, 枚举和String。其中byte/short/int本来就是整数,char本质上也是整数,而枚举类型也有对应的整数,String用于switch时也会转换为整数(通过hashCode方法)

为什么不可以使用long呢?因为跳转表值的存储空间一般为32位,容纳不下long;跳转表也没必要为long,一般用不上64位,不必浪费空间

循环本质

和if一样,循环内部也是靠条件转移和无条件转移指令实现的。比如说下面的代码:

int[] arr = {1,2,3,4};
for(int i=0;i<arr.length;i++){
    System.out.println(arr[i]);
}

其对应的跳转过程可能为:

  1. int[] arr = {1,2,3,4};
  2. int i=0;
  3. 条件跳转:如果i>=arr.length,跳转到第7行
  4. System.out.println(arr[i]);
  5. i++
  6. 无条件跳转,跳转到第3行
  7. 其他代码

在if中,跳转只会往后面跳,而for会往前面跳,第6行就是无条件跳转指令,跳转到了前面的第3行。break/continue语句也都会转换为跳转指令。

展开阅读全文

询问一个循环的问题和一个条件怎么想

03-20

假设一个简单的ATM机取款过程是这样的:首先提示用户输入密码,最多只能输入三次,超过三次则提示用户“密码错误,请取卡”结束交易。如果用户密码正确,再提示用户输入金额,ATM机只能输出100元的纸币,一次取钱数要求最低0元,最高1000元。如果用户输入的金额符合上述要求,则打印输出用户取的钱数,最后提示用户“交易完成,请取卡”,否则提示用户重新输入金额。假设用户密码是111111,则程序的运行结果如下:rn[img=https://img-bbs.csdn.net/upload/201703/20/1490018319_812701.png][/img]rn下面是我写的,总感觉被我复杂化+繁琐的,虽然看似解决了但是却怪怪的,于是问问对于这种循环该怎么写:rn[code=java] public static void main(String[] args) rn rn //先定义变量存放密码rn String pass;rn //定义变量存放输入的金额rn int money = 0;rn //定义一个变量,存放错误计数rn int error;rn //验证系统rn for (error = 0; error < 3;error ++ ) rn System.out.print("请输入密码:");rn pass = new Scanner(System.in).next();rn if (pass.equals("111111")) rn rn break;rn elsern //第三次密码错误的输出rn if (error == 2) rn System.out.println("第三次输入错误,请退卡");rn rn elsern System.out.println("输入错误,请重新输入:" );rn rn rn rn rn rn //取款部分rn //判断是否取款rn if (error < 2) rn //循环判断输入金额是否是0-1000元,并且只能取100的整数,比如100 200 300....1000rn for (int i = 0;i < 10; i++) rn System.out.print("请输入取款金额:");rn //输入取款金额rn money = new Scanner(System.in).nextInt();rn if (money == 0 ||rn money == 100 ||rn money == 200 ||rn money == 300 ||rn money == 400 ||rn money == 500 ||rn money == 600 ||rn money == 700 ||rn money == 800 ||rn money == 900 ||rn money == 1000 rn ) rn System.out.println("你取了" + money + "元");rn break;rn elsern System.out.println("输入错误,请重新输入");rn rn rn rn rn [/code]rn问题1:在输入100的整数条件是哪种,我自己考虑过用%运算符来取各个位数判断,也就是%和/混合使用:rn//取个位rnmoney % 10rn//取十位rnmoney /= 10rnmoney % 10rn 这样,但是感觉貌似不行,仔细纠结了下(感觉像是思考,实则什么都没想,又像下棋来比喻的化,我走了一步,完全考虑不到后面,没头绪,空)。rnrn问题2:验证的话,由于程序由上往下,我考虑到在输入的循环里面用if else 来判断,if里面完成输入和金额都做完,正确的做法一次性做完,嵌套循环,不跳出。else则包括所有的错误,但是个人感觉这样的代码阅读性非常差。并且个人就像是前面判断正确进入下一个“界面”,而不是在一个里面打转,但是我不这样做呢,代码顺序执行下来哪怕错的判断在下面语句不加判断的话也会执行下面的,这样就和正常逻辑来说不正确。rnrnrn 论坛

没有更多推荐了,返回首页