goto语句的解释
在C语言中,goto
语句是一种被称为跳转语句的工具。
它被用来无条件地跳转到代码中的其他标签处,从而改变程序的执行流程。
一般来说,程序员很少使用goto
语句,因为它会降低代码的可读性并增加程序的复杂性。
语法
goto label;
示例
下面是一个简单的示例,演示了如何在C语言中使用goto
语句。
假设我们打开了Visual Studio并创建了一个名为”goto”的项目,在该项目中创建了一个名为goto-statement.c
的源文件,其代码如下:
#include
void main()
{
int age;
gotolabel:
printf("You are not eligible to vote!\n");
printf("Enter you age:\n");
scanf("%d", &age);
if (age else
{
printf("You are eligible to vote!\n");
}
}
执行上面代码,得到以下结果
You are not eligible to vote!
Enter you age:
12
You are not eligible to vote!
Enter you age:
18
You are eligible to vote!
为什么它这么不受待见?
二十几年前,当计算机编程尚处于起步阶段时,程序流程是由 “GOTO” 语句来控制。
该类语句允许程序员对当前代码行断行,而直接进入另一个不同的代码段。
列表 1 为简单的示例。
图片
编程语言终究开始引入了函数的概念,即允许程序对代码进行断行。
如果已经完成,不再使用 goto 语句来表示代码的断行。
函数调用后,函数将回到下一条指令。列表2 为示例。
图片
这一做法改善了程序结构,提高了可读性。自此,这被视为编写程序的正确方法。
只要看到或想到 goto 语句,就会让软件工程师退缩,产生本能的厌恶。
在 wikipedia 上的解释就是;
GOTO语句一直是批评和争论的目标,主要的负面影响是使用GOTO语句使程序的可读性变差,甚至成为不可维护的「面条代码」。
随着结构化编程在二十世纪六十年代到七十年代变得越来越流行,许多计算机科学家得出结论,即程序应当总是使用被称为「结构化」控制流程的命令,以及 if-then-else
语句来替代 GOTO。
甚至在今天,许多程序风格编码标准禁止使用 GOTO 语句。
也有不少人为 GOTO 语句辩护,他们认为只要加以限制地使用 GOTO 语句不会导致低质量的代码,并且在许多编程语言中,一些功能难以在不使用 GOTO 语句的情况下实现。
比如有限状态机的实现、跳出嵌套循环以及异常处理等等。
大概最著名的对于 GOTO 的批评是艾兹格·迪杰斯特拉(Edsger Wybe Dijkstra)在1968年的一篇名为《GOTO陈述有害轮》的论文。
迪杰斯特拉认为不加限制地使用GOTO语句应当从高级语言中废止,因为它使分析和验证程序正确性(特别是涉及循环)的任务变得复杂。
另外一种观点出现在高德纳的Structured Programming with go to Statements
[3]中,文章分析了许多常见编程任务,然后发现其中的一些使用GOTO将得到最理想的结构。
限制GOTO
许多语言,如 C 语言和 Java,提供了相关的控制流语句,如 break
和 continue
,它们都是有效地被限制的 goto 语句。
它们的作用是无条件跳转,但是只能够跳到循环块结束的位置——继续进入下一循环(continue)或者结束循环(break)
switch/case结构
C 语言、C++ 和 Java 中的 switch 语句高效地实现了一个多路 goto,跳转目标由表达式的值来选择。
这也导致了我们没有不得不使用 goto 的理由。
针对这些,导致目前 goto 的使用情况是这样的:
goto 语句的结果:在C/C++等高级编程语言中保留了goto语句,但被建议不用或少用。
在一些更新的高级编程语言,如 Java 不提供 goto 语句,它虽然指定 goto 作为关键字,但不支持它的使 用,使程序简洁易读;
尽管如此后来的 c# 还是支持 goto 语句的,goto 语句一个好处就是可以保证程序存在唯一的出口,避免了过于庞大的 if 嵌套。
另一方面,goto 语句只是不提倡,当然不是禁用,那么在什么情况下可以使用 goto 语句呢?
可以考虑使用 goto 的情形:
-
从多重循环中直接跳出 ; -
出错时清除资源; -
可增加程序的清晰度的情况。
不加限制地使用 goto:破坏了清晰的程序结构,使程序的可读性变差,甚至成为不可维护的”面条代码”。
经常带来错误或隐患,比如它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句。
下列关于使用 goto 语句的原则可以供读者参考。
-
使用 goto 语句只能 goto 到同一函数内,而不能从一个函数里 goto 到另外一个函数里。 -
使用 goto 语句在同一函数内进行 goto 时,goto 的起点应是函数内一段小功能的结束处,goto 的目的 label 处应是函数内另外一段小功能的开始处。 -
不能从一段复杂的执行状态中的位置 goto 到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。 -
应该避免像两个方向跳转。这样最容易导致”面条代码”。
阅读过 linux 内核代码的同学应该注意到,linux 内核代码里面其实有不少地方用了 goto 语句,
这是在/drivers/i2c/i2c-dev.c
中的i2c_dev_init
函数:
static int __init i2c_dev_init(void)
{
int res;
pr_info("i2c /dev entries driver\n");
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class))
{
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
pr_err("Driver Initialisation failed\n");
return res;
}
但是你会发现,这些地方的goto
语句,使用非常谨慎,基本都遵循上面提到的几个原则。
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !