如何阅读代码

写这篇文章之前,我脑海中实际上想总结的内容是如何快速的定位到一个别人项目中的BUG。就个人经验来说:找自己代码中的BUG,要比找别人代码中的BUG简单;找目前在维护的代码的BUG,比找半年以前写的代码的BUG容易。这些现象的背后揭示的道理是,一段代码,你理解的越是透彻,你能找出其中的问题的概率越大。所以在谈怎么样快速定位到一个BUG之前,想先聊聊怎么样去读懂别人写的代码。

据说绝大部分的程序员宁可自己重写,也不愿意去维护别人写的代码。这其中有部分原因是有很大一部分程序员都觉得自己的水平比别人高(虽然很多人不好意思承认这一点),另外一部分原因在于,有时候要读懂别人的代码很难,花费的代价比自己重写一遍还大。那么,到底是什么东西在阻碍我们读懂一段代码,我们又该如何去客服这些困难呢?

你读不懂他的代码

这种情况其实很少出现,我们通常不会需要跨语言去找BUG(全栈除外),但是同一门语言的不同版本还是有可能会碰到的,比如下面这两段C++代码,可能会有部分C++程序员看不懂:

1
auto [itelem, success] = mymap.insert(std::pair(’a’, 100));
1
2
3
4
5
int foo(a)
int a;
{
return 0;
}

其中第一段是C++17中定义变量的语法,而第二段是C语言K&R版本定义函数的语法。维护别人的程序很多时候碰到的情况是第二种,你可能遇到一些过时的写法。

TIPS:持续学习

无论学什么语言都需要不断的学习,因为我们用到的绝大部分语言都还在在不断的演化中。

你理解不了他的代码

绝大部分人,如果说自己的读不懂一段代码,实际上都是在说,这段代码他无法理解。很多时候我们清楚的知道每一条语句在说什么,但是就是理解不了整段代码的作用(这种感觉就像读一段英文,每个单词你都懂,但是你不知道整句话在说什么)。比如下面这个摘自我最近一个项目中的函数:

1
2
3
4
5
6
void SystemInfoGetUUID(const char* id, ResultHandler handle_result,
const char* /* params */) {
const ResultType& result__ = SystemInfoGetUUID__();
auto result = StandardizeResult(result__, id);
handle_result(id, result.data());
}

你可能看懂了每一条语句(毕竟没几行),但是不一定明白这段代码的真正含义是什么,因为你不了解这段代码的作用是什么?

代码,大到整个项目,小到一个函数,广义上你都有可以理解为是算法——解决问题的步骤,所以理解一段代码你首先要清楚他要解决的问题是什么,如果我告诉大家,上面这个函数之所以这么写是因为我们需要把所有的返回值统一成一个标准格式的JSON串返回给调用方,理解上面这个函数大概会简单得多。

找出一段代码的作用有很多种方式:

  • 问作者,如果代码的原作者就在你的身边,不要犹豫,直接问。
  • 看文档,大部分成熟的项目都会有完善的文档,而大部分文档说的就是代码在做什么。
  • 增加上下文,如果项目没有文档,或者文档中没写你想读懂的那一部分代码的作用,你可以尝试把这段代码放到更广阔的上下文中去理解。大部分程序或者说函数都有输入输出,也就是这里说的上下文,找到它的上下文,通常对于理解它的作用会有很大的帮助。
  • 忽略细节,成大事不拘小节,读大的项目代码也是一样的。如果你知道一个函数的功能,有时候你可以直接忽略掉它的实现。

TIPS: 找上下文(输入、输出)

关于找一段程序的上下文,我了解到方法主要有下面这些:

看单元测试

找这个函数的单元测试,非常有效的一种方式,因为测试用例中通常给出了各种输入并检查对应的输出。可惜这种方法适用范围不广,因为它的前提是这个函数或者模块有测试用例。

代码跳转工具

使用代码跳转工具,查找调用这个函数的地方在哪里。这种方式通常情况下会有多个匹配点,一方面会增加你阅读的困难,另一方面却可以给你更多的参考例子。

有些工具可以生成函数调用关系图(比如C++中的doxygen+graphviz),从图中找上下文有时比在代码中直接找要简单很多。

从main函数开始读

这是迫不得已的情况下的大招,因为程序的入口通常是mian函数,所以你想查找的函数,总会有某一条来自main函数的执行路径(如果你写的是库,通常main函数在测试代码中)。

上面两个方式实际上是一种倒推的思路,而这个方法实际上的逻辑是正向的暴力穷举,它可能会耗费很长的时间,但在项目比较小或者时间比较充足的情况下很有用,它实际上相当于模拟计算机的执行过程。这个步骤可以考虑借助调试器,单步跟踪,一步一步的往下读,让调试器去帮你记住代码的上下文。

你理解不了他为什么这样写他的代码

每个程序员会有自己习惯的编码风格,就是每个人写出来的字都有自己的影子一样。不理解他为什么这么写,其实对于代码的维护并不会带来太大的问题,所以遇到这种情况,最好克制自己修改它的冲动,除非其中确实有BUG。

除此之外,我们能做的另外一件事情就是去学习或者建立你们团队的编码规范,因为它的存在会大大的减少你读不懂别人为什么这样写的可能性。


参考:

【1】:https://www.quora.com/How-do-I-learn-to-read-code 【2】:https://selftaughtcoders.com/how-to-quickly-and-effectively-read-other-peoples-code/