良许Linux教程网 干货合集 嵌入式C中的 goto 语句,争议很大

嵌入式C中的 goto 语句,争议很大

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 为简单的示例。

image-20240404211407958
image-20240404211407958

图片

编程语言终究开始引入了函数的概念,即允许程序对代码进行断行。

如果已经完成,不再使用 goto 语句来表示代码的断行。

函数调用后,函数将回到下一条指令。列表2 为示例。

image-20240404211419083
image-20240404211419083

图片

这一做法改善了程序结构,提高了可读性。自此,这被视为编写程序的正确方法。

只要看到或想到 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,提供了相关的控制流语句,如 breakcontinue,它们都是有效地被限制的 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 语句的原则可以供读者参考。

  1. 使用 goto 语句只能 goto 到同一函数内,而不能从一个函数里 goto 到另外一个函数里。
  2. 使用 goto 语句在同一函数内进行 goto 时,goto 的起点应是函数内一段小功能的结束处,goto 的目的 label 处应是函数内另外一段小功能的开始处。
  3. 不能从一段复杂的执行状态中的位置 goto 到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。
  4. 应该避免像两个方向跳转。这样最容易导致”面条代码”。

阅读过 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”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部