c语言入门13,突破重重包围,超强跳转语句指哪去哪,goto语句介绍

在第10节和第11节,我们介绍了 C 语言中的循环语句。上一节还介绍了初学者拿到实际问题,逐步解决问题,写出代码的思路。那作为练习和巩固,来考虑一下这个问题:

利用 C 语言编程,在终端打印出 100 以内的素数。(素数是只能被 1 和他本身整除的数。)


按照上一节介绍的开发思维,我们逐步把问题简化:首先要确定的是,程序需要从 1 加一逐步遍历到 100,这里需要一个循环;接着,对于每一个数 n,都需要从 2 遍历到 n-1,测试 n 能否被它整除,只要有一个数能, n 就不是素数,否则 n 就是素数,这里也需要一个循环。规划好了总体流程,接着就可以在脑海里,或者在编辑器里粗略的写出以下伪代码:

n 从 1 遍历到 100{
    m 从 2 遍历到 n-1{
        如果 n%m 等于 0
            break;
    }
    如果 m == n
        n 是素数
    否则
        n 不是素数
}

总体逻辑还是比较简单的,如果 n 不是素数,那么在 n-1 之前,总有一个 m 使得 n除以m 的余数为 0, m 的遍历在到达 n-1 之前就结束了。如果从 2 到 n-1 都没有一个 m 可以使 n除以m 等于0,则里面那个遍历结束后,m 应该是等于 n 的。所以最后只需要判断 m 是否等于 n 就可以判断 n 是不是素数了。

好了,思路理清了,可以写代码了:

#include <stdio.h>

int main(void)
{
    int i, j;
    for (i = 1; i <= 100; i++) {
        for (j = 2; j < i; j++)
            if (i % j == 0)
                break;
        if (j == i)
            printf("%d\n", i);
    }
    return 0;
}


可以看出,程序其实是两个循环套在一起的,很多程序员称这种循环为“嵌套循环”。内循环的循环变量不能再用 i ,而是改用 j,因为 i 已经被外循环使用了。除了打印一列数据之外,用循环还可以打印表格式的数据,比如打印小九九乘法表:

#include <stdio.h>

int main(void)
{
    int i, j;
    for (i=1; i<=9; i++) {
        for (j=1; j<=9; j++)
            printf("%d\t", i*j);
        printf("\n");
    }
    return 0;
}

内循环每次打印一个数,数与数之间用 Tab(制表符)隔开,外循环每次打印一行。结果如下:

在有嵌套循环的情况下,break 只能跳出最内层的循环或 switch 语句,continue 也只能终止最内层循环并回到该循环的开头。那有时就麻烦了啊,如果使用多层嵌套循环,最里面的循环有条语句出错了,我需要跳出整个嵌套循环做错误处理,还得挨个判断,一个一个的写 break,岂不是麻烦死了?

条件变量=0;
for(...){
    for(...){
        for(...){
            ...
            if(错误){
                条件变量 = 1;
                break;
            }
        }
        if(条件变量==1)
            break;
    }
    if(条件变量==1)
        break;
}
if(条件变量==1){
    错误处理;
    return 出错了;
}
return 正常;

可以看出,整个代码有很多重复代码,非常臃肿。

好在,C 语言提供了 goto 语句,能够实现无条件跳转

使用 goto 语句来写上述错误处理代码就简洁多了:

for(...){
    for(...){
        for(...){
            ...
            if(错误){
                goto error;
            }
        }
    }
}
return 正常;

error:
    错误处理;
    return 出错了;

甚至连条件变量都不用了。如果程序正常,执行到 “return 正常;”语句,直接就返回了。如果程序出错,那就会直接跳转到 error 处,执行错误处理语句,再返回 “出错了”。这里的 error: 叫做标号,给标号起名字也遵循标识符的命名规则。事实上我们在“switch语句”一节学过的 case和 default 后面也是跟一个冒号,在语法结构中也起标号的作用。

goto 语句过于强大了,从程序中的任何地方都可以无条件跳转到任何其它地方,只要给那个地方起个标号就行,唯一的限制是 goto 只能跳到同一个函数的某个标号处,而不能跳到别的函数里。所以,滥用 goto 语句会使程序的控制流程非常复杂,可读性很差。

著名的计算机科学家Edsger W. Dijkstra最早指出编程语言中 goto 语句的危害,提倡取消 goto 语句。因为 goto 语句不是必须的,goto 语句能解决的问题,也能用其他手段解决,上面我们给出了例子。不过 goto 语句说到底只是工具,本身并没有危害,危害都是程序员滥用造成的。通常 goto 语句只用于在函数末尾做出错处理(例如释放先前分配的资源、恢复先前改动过的全局变量等),比较上面两种写法,用 goto 语句还是方便很多。但是除了这个用途之外,在任何场合都不要轻易考虑使用 goto 语句。

事实上,linux 内核有大量使用 goto 语句做错误处理的代码。
阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif