作者丨胡译胡说
Linus Torvalds 无疑是开源软件界最具影响力的人物之一。作为 Linux 内核的创始人,他因技术贡献赢得了尊敬,但也常因口无遮拦的言辞引发争议。
Linus 对代码质量的要求极其严苛,也许正是因为自信能够写出完美的代码,才让他有底气挖苦和讽刺其他开发者吧。
Linus 写出的代码到底能有多么精简、多么高深、多么优雅、多么健壮……?可能很多程序员都对此充满好奇。Linux 内核的代码显然过于复杂,不适合“鉴赏”。不过,好在 Linus 还在 2005 年 4 月(和 BitKeeper 闹翻之后)编写过 Git 的原型,而且 Git 最初版本的源代码(https://github.com/git/git/commit/e83c5163316f89bfbde7d9ab23ca2e25604af290)只有千行左右,算上 README 和 Makefile 才只有 11 个文件。这可真是揭晓答案的好机会。
$ wc -l *.c *.h | sort
23 cat-file.c
43 read-tree.c
51 init-db.c
66 write-tree.c
81 show-diff.c
93 cache.h
172 commit-tree.c
248 update-cache.c
259
read-cache.c
1036 total
不过,要是把这些源文件通读一遍还是需要花些功夫的。那就再从中挑一个最简单的来看一看。毕竟齐白石画只虾、画个萝卜也是名作。
最简单的应该是 init-db.c
了(别光看 cat-file.c
和 read-tree.c
的行数少,那里面调用的函数可复杂了)。经过编译后, init-db.c
形成了一个叫作 init
-db
的命令。
试着运行一下就会发现,该命令的功能非常单一,类似现在的 git init
,仅仅是先在当前目录中创建一个隐藏目录 .dircache
,然后在里面创建 objects
目录,以及以两位十六进制数命名、名称依次为 00
~ ff
的 256 个子目录。
对照着功能再来欣赏下 Linus 亲自操刀的 init-db.c
的代码,
代码非常直观,①先调用 mkdir()
创建了 .dircache
,然后创建了 .dircache/
objects
②,最后利用 for
循环,创建了 256 个名字依次为 00
~ ff
的、 .dircache/objects
中的子目录。
但请注意这一行 path=malloc(len+40);
。这里动态分配了一块内存,大小为 len+40
,用于存储以十六进制数命名的子目录的路径。
稍有 C 语言编程经验的程序员都会牢记一条法则,如果使用 malloc()
分配了内存,但忘记调用 free()
来释放,就会造成内存泄漏。只要大学还在使用谭浩强的 C 语言教材开蒙,就是毫无项目经验的大一新生,也会因多次听到老师强调这一点,而囫囵吞枣地记住要 free()
吧。大名鼎鼎的 Linus 竟然忘了?
而且,搜索整个项目,可以发现不止一处调用了 malloc(),calloc(),realloc()
来申请内存,
却只 free()
过 1 次!
Linus 是真忘了?这不妥妥地内存泄露吗!
等一等, malloc()
之后真的一定要 free()
吗?
其实,如果程序运行时间很短(就像 init-db
这样简单的工具),程序结束后操作系统就会回收所有已分配的内存(包括未释放的堆内存),所以不手动释放也不会对系统产生显著影响(但确实是不好的编程习惯)。
而 Linus 在初版 Git 中唯一一次 free()
是在 for
(i=0;i<entries;i++){
这样一个循环中调用的。这说明即使程序不会长时间运行,但如果分配了大量内存或通过循环多次申请内存,还是应该及时 free()
,及时释放资源给其他进程。
总之, malloc()
之后并不是一定需要 free()
,这取决于具体的场景和需求。下回面试时万一忘记写 free()
,就可以辩解了,“Linus 当初创造 Git 时也没写,我们这是特意不写的”。但结果嘛……祝你好运。
不过,还有个好奇的点, path=malloc(len+40)
这里为啥要
+40
呢,追加的 /[0-f][0-f]
明明只占 3 个字节呀。
