news 2026/8/6 21:58:32

C语言 goto 还能用吗?为什么 Linux 内核仍然大量使用它

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言 goto 还能用吗?为什么 Linux 内核仍然大量使用它

引言

在 C 语言中,很少有哪个关键字像 goto 一样引发如此多的争议。

几十年来,程序员一直被告知要避免使用它。很多教材甚至直接总结为一句话:

“永远不要使用 goto 。”

这种观点很大程度上源于 Edsger W. Dijkstra 于 1968 年发表的著名论文 《Go To Statement Considered Harmful》 。

然而,如果你去阅读 Linux Kernel 的源代码——这个世界上规模最大、最成功的软件项目之一——你很快就会发现一个令人意外的事实:

goto 几乎随处可见。

这是否意味着 Linux Kernel 无视现代软件开发实践?

当然不是。

真实情况远比这个复杂。

真正的问题从来都不是 goto 本身

Dijkstra 并没有认为所有 goto 的使用都是错误的。

他批评的是 无结构的控制流(unstructured control flow) ,也就是人们常说的 spaghetti code (面条式代码):程序执行流程在一个函数中毫无规律地跳来跳去。

例如:

gotoLabelA; ... LabelA: ... gotoLabelC; ... LabelC: ... gotoLabelB;

当程序可以跳转到几乎任何位置时,代码就会变得非常难以理解,也很难验证它是否正确。

现代软件工程一直反对这种写法,因为它会降低代码的可读性、可测试性以及可维护性。

真正的问题是 无结构的跳转 ,而不是 goto 这个关键字本身。

为什么 Linux Kernel 推荐使用 goto

Linux Kernel 官方文档专门讨论过这个问题。

它并没有禁止 goto ,相反,它建议将 goto 用于 集中式资源清理(centralized cleanup)和错误处理(error handling) 。

Coding Style 中指出,当一个函数有多个退出点,并且这些退出点都需要执行相同的清理工作时, goto 会非常有用。( Linux Kernel Documentation )

与其在每一个错误判断后重复写清理代码,不如统一跳转到一个清理标签。

例如:

char *buffer = kmalloc(...); if(!buffer) return-ENOMEM; if(condition) gotoout_free_buffer; ... out_free_buffer: kfree(buffer); returnret;

这种写法有几个明显的优点:

在 C 中,资源管理并不容易

goto 之所以至今仍然有价值,最主要的原因是 C 本身没有自动资源管理机制。

一个函数可能需要申请多种资源,例如:

假设一个函数按顺序申请了这些资源:

Allocate A Allocate B Allocate C Allocate D

如果申请 D 时失败,那么程序必须按照相反的顺序释放资源:

Free C Free B Free A

如果没有统一的清理机制,那么每一条错误处理路径都不得不重复写几乎相同的资源释放代码。

随着函数越来越大,这种代码很快就会变得难以维护。

CERT C 也推荐这种模式

CERT C Secure Coding Standard 得出了相同的结论。

它的 MEM12-C 指南建议:当函数在出错时需要释放多个资源,可以使用 goto chain 来完成统一清理。( CMU SEI )

与其在每一个错误处理分支中重复编写清理代码,不如把清理逻辑组织成一组标签,每个标签负责释放一层已经申请的资源,并按照相反的顺序逐步完成清理,最后退出函数。

这种模式有几个优点:

需要注意的是,CERT 强调,这种建议仅适用于 单个函数内部的局部错误处理 ,并不是鼓励在整个程序中随意使用 goto 。

Linux Kernel 中的真实代码

这并不仅仅是理论上的建议。

Linux Kernel 本身就有大量函数采用了多级清理标签。

历史上, copy_process 一直都是这种设计模式最经典的例子之一。虽然不同版本的 Linux Kernel 中,清理标签的名称已经发生了一些变化,但在今天的 kernel/fork.c 以及大量驱动代码中,仍然可以看到大量采用向前跳转(forward goto )的清理链。( SEI Wiki )

典型的标签包括:

gotobad_fork_cleanup_mm; gotobad_fork_cleanup_fs; gotobad_fork_cleanup_io;

每一个标签只负责释放在当前阶段之前已经成功申请的资源。

虽然这个函数包含了很多 goto 语句,但整个控制流仍然是有结构的,因为所有跳转都是 向前 进入统一的清理区域,然后退出函数。

为什么现代 C++ 很少需要 goto

C++ 通过 RAII(Resource Acquisition Is Initialization) 基本解决了这个问题。

对象离开作用域时,会自动释放自己所持有的资源。

例如:

std::unique_ptr std::vector std::string std::lock_guard

因此,大多数情况下都不再需要专门的清理标签。

即使发生异常(exception),对象的析构函数也会自动执行。

因此,在现代 C++ 中,很少再需要使用 goto 来完成资源管理。

语言本身已经提供了更加安全的解决方案。

什么时候应该使用 goto ?

合理的使用场景包括:

不合理的使用场景包括:

有一个简单的判断原则:

如果所有 goto 都是为了跳转到同一个清理区域,那么这种写法通常是可以接受的。

如果程序执行流程在函数中到处跳来跳去,那么很可能就是代码设计出了问题。

总结

goto 本身既不是好的,也不是坏的。

1968 年那篇著名论文批评的是混乱、无结构的程序,而不是规范的错误处理方式。

如今,Linux Kernel 作为世界上最大的 C 项目之一,仍然大量使用 goto ,因为它解决了一个非常现实的工程问题:在没有自动资源管理机制的语言中,实现统一的资源清理。( Linux Kernel Documentation )

现代 C++ 已经通过 RAII、智能指针以及确定性的析构机制,在很大程度上取代了这种模式。

但对于现代 C 来说,尤其是在系统编程、嵌入式开发以及操作系统内核中, goto 仍然是一个重要且实用的工具。

和任何语言特性一样,它真正的价值,并不取决于关键字本身,而在于你为什么使用它,以及如何使用它。

引用链接

Linux Kernel Documentation: https://www.kernel.org/doc/html/latest/process/coding-style.html "Linux kernel coding style — The Linux Kernel documentation"

CMU SEI: https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/recommendations/memory-management-mem/mem12-c/ "MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources | CERT Secure Coding"

SEI Wiki: https://wiki.sei.cmu.edu/confluence/x/odYxBQ "MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources - SEI CERT C Coding Standard - Confluence"

Linux Kernel Documentation: https://www.kernel.org/doc/html/latest/process/coding-style.html "Linux kernel coding style — The Linux Kernel documentation"

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 21:57:57

Win11 24H2正式版更新与安装全指南

1. Win11 24H2正式版更新深度解析微软最新发布的Windows 11 24H2版本(Build 26100.7627)标志着这个操作系统进入了一个更成熟的阶段。作为一名长期跟踪Windows系统更新的技术博主,我第一时间获取并测试了这个多合一ISO映像,下面将…

作者头像 李华
网站建设 2026/8/6 21:55:42

ADR漏洞修复指南:常见安全问题解决方案

ADR漏洞修复指南:常见安全问题解决方案 【免费下载链接】ADR ADR secures enterprise AI agents through observability, security benchmarking, and threat detection. Deployed at Uber. 项目地址: https://gitcode.com/GitHub_Trending/adr10/ADR ADR&am…

作者头像 李华
网站建设 2026/8/6 21:54:47

企业GEO持续复测如何工程化?测试字段、版本管理与验收规则

企业GEO持续复测可以被工程化为四类对象:企业事实版本、固定测试问题、公开内容版本和AI回答记录。没有这些基础字段,所谓监测很容易退化为偶尔截图,无法判断内容修改与AI答案变化之间是否存在关系。万视AI当前重点推进企业GEO定制服务&#…

作者头像 李华
网站建设 2026/8/6 21:50:40

C语言学习:数据结构篇 Day17

一. 基本概念数据结构 ---- 用来干啥的?---- 数据与数据之间的关系 以及相关的操作 基本概念: 数据:符号表示 数据元素:有一定意义的数据 数据项:数据元素包含更具体的数据 数据对象:性质相同的数…

作者头像 李华
网站建设 2026/8/6 21:49:15

从论文到实践:IBM TTM 模型核心创新点解析与代码实现案例

从论文到实践:IBM TTM 模型核心创新点解析与代码实现案例 【免费下载链接】ttm-research-r2 项目地址: https://ai.gitcode.com/hf_mirrors/ibm-research/ttm-research-r2 IBM TTM(Tiny Time Mixer)是由IBM Research开源的紧凑型预训…

作者头像 李华