3.11 认识标号语句和goto语句
在C语言里,有很多方法可以改变语句的执行顺序和流程,比如while语句、if语句和return语句。while语句可以反复执行一段代码,if语句可以选择不同的分支,return语句可以立即返回到函数的调用者。
在函数cusum的以下版本中,我们没有使用while语句,而是用新的语句类型来替代它的功能,它们分别是标号语句和跳转语句。
unsigned long long int cusum(unsigned long long int r) { unsigned long long int sum = 0; if(r == 0 || r > 1000000000)return 0; again: sum += r; r --; if(r)goto again; return sum; }
说起来,跳转语句是你的老朋友了,因为跳转语句有好几种,而return语句就是其中之一。我们现在所用的,是另一个跳转语句,也就是goto语句,它可以在包含它的函数内跳来跳去,其语法为
goto标识符;
goto语句由关键字“goto”、标识符和分号“;”组成,既然是跳转,那肯定得有一个目标,这里的标识符用于指示跳转的目的地。在以上函数中,语句
goto again;
就是跳转语句,标识符again用于指定跳转的目标位置。反过来,为了指定跳转的目标,需要在目标语句前做一个记号或者标记,我们称之为标号。这个标号和它后面的语句一起,共同组成一种新的语句类型:标号语句。因此,标号语句的语法为
标识符: 语句
这就是说,由标识符、冒号“:”和语句组成的新语句,称为标号语句。在以上函数中,语句
again: sum += r;
就是标号语句。
现在我们把这两种语句连缀起来,看看它们在以上函数里是如何运作的。标号本身不影响程序的正常执行,就像它不存在一样。我们首先判断变量r的值,如果为0,或者大于1000000000,则直接返回一个0给它的调用者;如若不然,则将变量r的当前值加到变量sum,然后将r的存储值递减。显然,标号的存在不影响程序的正常执行。
在接下来的if语句中,判断变量r的存储值是否已经递减到0,如果不为0,则跳转到标号again处执行。标号语句执行完后,将继续往下执行其他语句,直至又一次来到if语句,再次判断变量r的值。如果变量r的值依然不为0,则再次跳转;如果为0,则不执行goto语句,且离开if语句往下面执行。
相比之下,为了使代码简洁,我更愿意使用逗号表达式。在函数cusum的以下版本中,我们把if语句的控制表达式改了一下,但程序的功能不变。我们在前面已经把逗号表达式讲得很清楚了,这里不再重复,请读者自行分析。
unsigned long long int cusum(unsigned long long int r) { unsigned long long int sum = 0; if(r == 0 || r > 1000000000)return 0; again: if(sum += r --, r)goto again; return sum; }
显然,我们这里的标号语句是:
again: if(sum += r --, r)goto again;
但这个标号语句包含了一个goto语句。
根据语法可知,标号和冒号“:”之后只能是语句(单条语句或者复合语句),而不能是声明。所以,在以下版本的cusum函数中包含了一个错误,即,标号语句的冒号“:”之后不是语句,而是一个声明。
除此之外,函数的其他部分没有什么问题,而且程序的工作流程是清晰的:先是判断变量r的值,如果大于0且小于1000000000则跳转到标号next处执行,否则就直接返回0到函数的调用者。
在标号next处,声明了变量sum并将它初始化为0,然后使用while语句完成累加过程,最后返回累加和(也就是变量sum的存储值)到调用者。当然,我们已经说过,冒号之后不应该是声明,所以在程序的翻译阶段,翻译器将报告错误并停止翻译。
unsigned long long int cusum(unsigned long long int r) {
if(r > 0 && r <= 1000000000)goto next; return 0; next: unsigned long long int sum = 0; while(r)sum += r --; return sum; }
要使这个函数能够正常通过翻译,就得把冒号后面的声明改为语句。说起来简单得难以置信,只需要在冒号后面加一个分号“;”,也就是添加一个空语句,就可解决。以下是修改后的cusum函数。
unsigned long long int cusum(unsigned long long int r)
{
if(r > 0 && r <= 1000000000)goto next;
return 0;
next:
;
unsigned long long int sum = 0;
while(r)sum += r --;
return sum;
}
另一个稍微麻烦一点的办法是使用复合语句,也就是用一对花括号将声明围起来,或者干脆连while语句也围在一起。
练习3.10
1.上面已经说了,另一个办法是使用复合语句,请自己上机尝试一下。
2.使用这几章已经学过的知识,编写一个计算阶乘的程序,使其可以计算10以内的正整数的阶乘,至少给出10种写法。