5

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

 3 years ago
source link: https://blog.popkx.com/introduction-to-c-language-13-break-through-the-surrounding-super-jump-statement-where-to-go-goto-statement-introduction/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

发表于 2018-11-12 08:11:26   |   已被 访问: 400 次   |   分类于:   C语言   |   暂无评论

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

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

bbdff344bb2d1fe6e9f14ab8aece9a86.png

按照上一节介绍的开发思维,我们逐步把问题简化:首先要确定的是,程序需要从 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 是不是素数了。

a6cd1ef4134fbe94e6d5d027c84c5a39.png

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

#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;
}
46a44fe910ba7362114ea62ade7bdf3e.png

可以看出,程序其实是两个循环套在一起的,很多程序员称这种循环为“嵌套循环”。内循环的循环变量不能再用 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(制表符)隔开,外循环每次打印一行。结果如下:

9ddd3a7c78e73f84cd707c43068280d3.png

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

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

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

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

fb0c9396949cfffc2b4342a43f259617.png

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

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

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

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

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

781b25194d3348a0e4a0af40be4293e3.png

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

事实上,linux 内核有大量使用 goto 语句做错误处理的代码。

阅读更多:   C语言


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK