找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3710

积分

1

好友

502

主题
发表于 7 天前 | 查看: 27| 回复: 0

有两点需要先明确:

  1. Linus Torvalds(林纳斯·托瓦兹)是一位成就非凡的工程师,他创造的 Linux 内核和 Git 系统深刻影响了整个计算领域。
  2. 和许多资深开发者一样,他对代码风格有着极其鲜明的个人主张。在 Linux 内核这样由全球数千人协作的巨型项目中,推行统一的编码规范至关重要;但在你自己的个人项目中,是否完全遵循他的观点则见仁见智。

那他到底为什么讨厌“数组形参”?

最核心的原因很简单:在 C 语言中,根本就不存在“数组作为函数参数”这回事!

当你写下这样的函数声明:

void foo(int arr[10]);

编译器并不会真的把一个长度为 10 的数组传进去。它会悄无声息地把声明转换为一个指针:

void foo(int *arr);

这种行为源于 C 语言早期的历史设计,如今虽然作为一种语法糖被保留了下来,但本质上是一种“障眼法”。正如 Linus 在 2015 年一封著名的邮件里直白地批评道:

“C 语言中根本不存在数组参数。可悲的是,出于一些糟糕的历史原因,编译器居然接受了这种写法,并默默地把它变成指针参数。支持这种写法的人,脑子都不太清醒。”

问题究竟出在哪里?来看一个真实案例

Linus Torvalds批评数组形参的邮件截图

来源:https://lkml.org/lkml/2015/9/3/428

在这封邮件中,他批评了一段来自无线网络驱动的代码:

static bool rate_control_cap_mask(..., u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
{
    for (i = 0; i < sizeof(mcs_mask); i++)
        ...
}

乍一看似乎没问题,但这里埋藏了两个致命的陷阱:

  1. mcs_mask 被声明为数组,实际上却是一个指针;
  2. sizeof(mcs_mask) 返回的是指针的大小(4或8字节),而不是数组的真实长度!

导致的结果就是:循环只会迭代 4 次(32位系统)或 8 次(64位系统),而它本应迭代 10 次(因为 IEEE80211_HT_MCS_MASK_LEN == 10)。虽然碰巧因为数组元素是单字节,且前几个元素可能就满足了条件,使得这个 Bug 在测试中没有立刻暴露,但这仍然是一个典型的逻辑错误

Linus 对此怒斥道:

“这段代码之所以看起来‘合理’,完全是因为第一个错误(用数组形参)掩盖了第二个错误(误用 sizeof)。它本质上是一堆互相喂养的垃圾。”

正确的写法应该是:

  • 参数直接声明为指针:u8 *mcs_mask
  • 循环边界明确使用宏常量:for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)

或者,如果你真想表达“这是一个固定长度的数组”,那就不要依赖形参语法,而是通过注释、命名或配套的宏(例如 Linux 内核中的 ARRAY_SIZE())来传达意图。理解这些底层原理,是掌握 C/C++ 编程的关键之一。

为什么这种写法危害更大?

Linus 特别强调:这种写法具有极强的欺骗性

  • 它看起来像是在“文档化”参数的预期长度(比如 [10] 仿佛在说“我需要一个10元素的数组”);
  • 但实际上,编译器完全忽略这个数字,不会做任何边界检查;
  • 更糟糕的是,它会诱导程序员写出 sizeof(arr) 这种看似合理、实则完全错误的代码。

“这根本不是文档,这是在撒谎。误导性的‘文档’比没有文档更危险。” —— Linus Torvalds

在他看来,使用 int arr[10] 作为函数参数,无异于向全世界宣告:“我并没有真正理解 C语言 的底层机制”。

那我们该怎么办?

  • 如果你在参与 Linux 内核或遵循其规范的项目:请严格避免数组形参,一律使用指针。
  • 在你自己的项目中:虽然技术上可以使用,但你必须清醒地认识到它只是“语法糖”,背后依然是裸指针。务必避免在函数内部使用 sizeof 来获取数组长度。
  • 更好的实践:显式传递数组长度作为另一个参数,或者使用结构体将指针和长度封装在一起,也可以借助 ARRAY_SIZE 这类宏来提高代码的可读性和安全性。这些都属于良好的 编程原则

Linus 反感“数组形参”,并非出于固执,而是因为:

  • 名不副实(声称是数组,实为指针);
  • 极易引发隐蔽的 Bug(尤其是配合 sizeof 使用时);
  • 制造了虚假的安全感,让开发者误以为编译器会帮忙进行边界检查。

正如他所说:“当我看到这种明显荒谬的代码时,我会非常愤怒——因为它让我担心那些我还没发现的、更加隐蔽的问题。”

所以,与其依赖这种“外表光鲜却暗藏陷阱”的语法,不如老老实实地使用指针,清清楚楚地传递长度——这才是对 计算机系统 底层行为有清晰认知的体现,也是真正的“懂 C”。





上一篇:深入解析ELF中的.data.rel.ro节:只读重定位数据与RELRO安全机制
下一篇:复杂事务处理:微服务Saga模式与SAP单体ACID的根本差异
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 11:44 , Processed in 0.845419 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表