如何理解大型复杂模块,"without"编译和调试

How to understand large complicated module, "without" compiling and debugging it

本文关键字:without 编译 调试 何理解 大型 复杂 模块      更新时间:2023-10-16

我有一个由多个文件组成的大模块,这个模块没有文档,没有适当的注释,而且非常复杂。。。它也有糟糕的变量名(如int x、char y等)

我本可以使用调试器和断点来查看模块的流程,但我没有完整的系统来编译这个模块。

所以我现在必须依赖于源代码本身。

有什么想法吗?

记录 ,并根据其实际行为进行验证。

您可以使用两种基本方法之一:自下而上或自上而下。

  • 使用自下而上的方法,您首先尝试查找自包含的函数。即不调用模块中其他函数的函数。它们可以调用标准库函数或递归,但除此之外,它们只与数据交互。试着理解它们的含义(并添加相应的注释,然后转到使用您已经理解的函数的函数

    问题是,用这种方法很难全面了解情况。

  • 使用自上而下的方法,首先尝试识别模块的接口功能。应该从外部调用的函数。然后,你试着猜测他们应该做什么,并试着推导出他们是如何使用内部函数来实现目标的。这允许您派生这些内部函数的作用,并且您可以向下递归调用树。这与调试器的方法非常相似。

    这样做的问题是,可能很难确定哪些功能是接口功能。许多(大多数)库/模块不需要区分接口和内部函数,因此.so文件会导出所有符号。然而,开发人员可能已经将所有接口函数收集到一个大标题中。如果是这样的话,您就有了一个现成的完整的接口函数列表,您可以从中启动。

我通常尝试将这两种方法结合起来,从自上而下开始,然后深入搜索我能够完全理解的自包含函数。这是将有关目的的信息与有关内部工作的信息相结合的最快方法。


理解代码的另一个非常重要的工具可能是重构。当我必须把自己读入一个包含50多行代码的函数时,在我理解它之后,它几乎不可避免地不再超过50行。

在这些情况下,我试图确定函数中相对独立的部分,试图理解它们的作用,并将它们分解为自己的函数。另一种启发式方法是寻找重复的代码,这些代码可以分解为一个用不同参数调用的函数。一旦一个函数被分解出来,你就知道它是做什么的,并且你有了一个有意义的名字,这使得长函数的其余部分更容易理解。使用它可以降低长函数的结构复杂性

当使用这种重构方法时,非常重要的是要限制自己只做那些你可以证明它们不会改变可见行为的更改:你还没有完全理解代码,所以如果你做了一些无关紧要的更改,你真的会把它搞砸。