C语言非常道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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种写法。