【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
无论是在判断还是在循环的过程中,通常在遇到合适的条件的时候就会退出相应的模块。跳出模块运行的方式很多,break,continue,return都可以。下面我们就可以分别对他们进行将介绍。
(1)continue只能用于循环,而break循环、判断都可以应用。两者有什么区别呢?
-
21:
for(
int m =
10; m <
100; m ++)
-
00401638 mov dword ptr [ebp
-4],
0Ah
-
0040163F jmp process+
2Ah (
0040164a)
-
00401641 mov eax,dword ptr [ebp
-4]
-
00401644
add eax,
1
-
00401647 mov dword ptr [ebp
-4],eax
-
0040164A cmp dword ptr [ebp
-4],
64h
-
0040164E jge process+
55h (
00401675)
-
22: {
-
23:
if(m /
20 ==
3)
-
00401650 mov eax,dword ptr [ebp
-4]
-
00401653 cdq
-
00401654 mov ecx,
14h
-
00401659 idiv eax,ecx
-
0040165B cmp eax,
3
-
0040165E jne process+
42h (
00401662)
-
24: {
-
25:
break;
-
00401660 jmp process+
55h (
00401675)
-
26: }
-
27:
-
28:
if(m /
10 ==
0)
-
00401662 mov eax,dword ptr [ebp
-4]
-
00401665 cdq
-
00401666 mov ecx,
0Ah
-
0040166B idiv eax,ecx
-
0040166D test eax,eax
-
0040166F jne process+
53h (
00401673)
-
29: {
-
30:
continue;
-
00401671 jmp process+
21h (
00401641)
-
31: }
-
32: }
-
00401673 jmp process+
21h (
00401641)
我们在循环模块里面可以看到了代码做了两次判断。一次判断是查看m除以20是否等于3,一次判断是查看m除以10是否可以整除。同样,前面的判断结果是break,后面的结果是continue,那么两者之间的区别是什么呢?我们可以好好看一下。在第一次条件判断的时候,我们发现如果m除以20不等于3,那么下面指令直接跳转到0x00401662,也就是第二条件的入口处。但是如果第一个条件判断成功了,那么程序就会跳转到地址0x00401675,也就是说整个循环模块已经结束了。而continue呢?我们发现不管条件判断是否为真,那么指令都会跳转到0x00401641处执行,也就是循环+1的地方。这说明continue结束的只是本次循环,整个循环的流程还没有结束,而break表示当前已经推出了整个循环模块。如果大家对书本上的概念还有些模糊的话,那么汇编指令在这里已经把细节之处解释得明明白白了。
(2)goto一般用的情况不多,但是有一种情况例外?
-
void process()
-
{
-
if(
/* get resources one */ ==
0)
-
{
-
return;
-
}
-
-
if(
/* get resource two */ ==
0)
-
{
-
/* free resource one */
-
return;
-
}
-
-
if(
/* get resource three */ ==
0)
-
{
-
/* free resource two */
-
/* free resource one */
-
return;
-
}
-
-
if(
/* get resource four */ ==
0)
-
{
-
/* free resource three */
-
/* free resource two */
-
/* free resource one */
-
return;
-
}
-
-
/* ... */
-
-
}
通常情况下,我们创建一个对象时,需要各种各样的资源,等到所有资源都分配齐了,我们的代码才能开始工作。但是事实上,也存在很多分配失败的可能性。如果遇到了分配失败,那么我们就要对原来的资源进行释放处理。随着资源的总数越来越多,这个代码块就会显得越来越臃肿。那么有没有什么好的办法解决这一难题呢?goto就是一种好的选择。
-
void process()
-
{
-
if(
/* get resources one */ ==
0)
-
{
-
return;
-
}
-
-
if(
/* get resource two */ ==
0)
-
{
-
-
goto fail_one;
-
}
-
-
if(
/* get resource three */ ==
0)
-
{
-
goto fail_two;
-
}
-
-
if(
/* get resource four */ ==
0)
-
{
-
-
goto fail_three;
-
}
-
-
/* ... */
-
fail_three:
-
/* free resource three */
-
-
fail_two:
-
/* free resource two */
-
-
fail_one:
-
/* free resource one */
-
return;
-
}
(3) return也是跳出代码段一个很好的方法。
它不光可以在函数中使用,也可以灵活运用在循环语句、判断语句中。要是在递归语句中,更是少不了他的身影。比如说,我们现在查看一个字符串是否满足这样一个结构:
str = ab | a str b;
那么换成函数代码应该怎么编写呢?大家可以自己先试一试,下面是我的一个方法:
-
BOOL process_multi_char(
const
char* str,
int length)
-
{
-
if(
2 == length)
-
return !strncmp(str,
"ab", strlen(
"ab")) ?
TRUE :
FALSE;
-
-
if(str[
0] ==
'a' && str[length
-1] ==
'b')
-
return process_multi_char(str+
1, length
-2);
-
else
-
return
FALSE;
-
}
-
-
BOOL process(
const
char str[],
int length)
-
{
-
if(
NULL == str || length <
2 || (
0 != length %
2))
-
return
FALSE;
-
-
return process_multi_char(&str[
0], length);
-
}
这里return的好处就是充分把字符串进行了拆分。我们按照定义,先从基本的问题下手,大事化小,小事化了。当然这里的小事就是字符串这有两个字符的情形,也是最底层函数的出口。要不然函数就会一致不停地处理下去,这是没有意义的。计算过程中一旦发现了不合法的运算,那么整个迭代的过程就会结束,快速回溯,输出结果。所以说要想写好递归或者是用到堆栈结构的代码时,必须考虑代码的入口和出口的把握,而return就是最好的选择,什么时候return,什么时候迭代都要把握时机,否则很容易陷入到死循环当中去。
【预告: 下面的博客是用汇编的眼光看C++之嵌入式汇编】