零. 课程要点:
- If-else语句
- switch-case语句
- do~while/while/for循环语句
- 循环与递归的比较
这一节主要介绍常见流程控制语句的机器级表示。有了前面的基础,这里的内容就比较简单。
一. If-else语句
if (cond_expr)
then_statement
else
else_statement
常见的用法有如下两种形式:
c = cond_expr;
if (!c)
goto false_label;
then_statement
goto done
false_label:
else_statement
done:
c = cond_expr;
if (c)
goto true_label;
else_statement
goto done
true_label:
then_statement
done:
举例有以下代码:
int get_cont( int *p1, int *p2 ) {
if ( p1 > p2 )
return *p2;
else
return *p1;
}
其对应的汇编语句如下:

二. switch-case语句
在执行switch语句时,会先根据取值生成跳转表,存储着跳转标签和取值的对应关系,如下图所示:

这里的实现有点绕,a的判断取值可能有10,12,14,15,17,和其它,可是跳转表生成了从10到17的对应标签,插了11,13,16。这是因为之后要使用jmp *.L8(, %eax, 4)
来跳转,也就是转.L8+4*i处,所以一进来就把a减10,使得i可以从0开始计算。并且把defalut情况优先处理掉。然后根据不同的a值,跳转到不同的标签处执行。
所以这里有一个问题,如果a的取值范围过于离散,如1,988,19999,那么这个跳转表就要插入很多的值,显得累赘过大,因此switch比较适合变量取值集中在某一范围之内的情况。
三. do~while/while/for循环

四. 循环与递归的比较
递归是个看起来很高大上的东西,递归思想也令人觉得很美妙,能够用简洁的语句实现复杂的循环判断语句。但在具体实现过程中,也许并不是很好的选择,只是让你的代码看上去牛逼了一点。
我们来看一个实现前n个整数相加的函数:
int nn_sum ( int n)
{
int i;
int result=0;
for (i=1; i<=n; i++)
result+=i;
return result;
}
上面用了循环的语句实现,因此只需要用比较指令和跳转指令实现,具体如下:

这个过程中只用到ebp用来取入口参数,局部变量i和result分别分配在edx和eax中,因此其栈帧仅占用4字节空间,若考虑栈帧按16B对齐,也仅用16字节。
那么用递归方式实现呢?
int nn_sum ( int n) {
int result;
if (n<=0 )
result=0;
else
result=n+nn_sum(n-1);
return result;
}
由于每一次递归都要调用一次函数,所以每次都要开辟一个栈帧,总共需要16n字节,空间的消耗很大。

除此之外,每次递归调用都要执行16条指令,一共执行了n次过程调用,共16n条指令,比循环方式多不少。
由此看出,递归精简了代码,但是对空间和指令的消耗更大。为提高程序性能,能用非递归方式执行则最好用非递归方式。不过递归思想还是要认真掌握的,随着计算机性能的发展,也许这点开销不算什么,程序猿们可以追求自己心中的美学。
网友评论