绘制函数调用图(call graph)(1):专栏开篇

绘制函数调用关系图(call graph),对开发人员理解源码有非常大的帮助,特别是在以下情况:

  • 大型项目,庞杂的代码量;
  • 项目文档缺失,特别是设计文档、流程图的缺失;
  • 第三方代码库,如开源项目;
  • 检查实际函数调用关系跟规划的设计是否一致,以免出错。

绘制函数调用关系图的途径主要有两种,一种是人工手动绘制(很多人应该都有一边看代码(或借助调试工具单步跟踪),一边在纸上画函数调用关系图的经历),另一种是使用工具自动绘制。

自动分析函数调用关系的工具,主要分为「静态分析」和「动态分析」两种。所谓静态分析是指在不运行待分析的程序的前提下进行分析,那么动态分析自然就是记录程序实际运行时的函数调用情况了。更详细的分类,可以参考以下文章:
https://blog.csdn.net/solstice/article/details/488865

动态调用图是精确的,但仅描述程序的一次运行。 静态调用图是旨在表示程序的每个可能运行的调用图。 确切的静态调用图是一个不可判定的问题,所以静态调用图算法通常是过度的。 也就是说,发生的每个调用关系都在图中表示,并且可能还有一些调用关系在程序的实际运行中永远不会发生。

以下网址列举了这方面的工具:
https://en.wikipedia.org/wiki/Call_graph

大部分的工具,都是将函数调用关系的分析结果以文本的形式输出,你还需要利用第三方工具将文本文件转为图片文件,大部分都是使用可视化工具 graphviz 转化为图片(这个转化过程涉及到 dot 语言)。

针对C/C++语言,我体验了几款开源的、静态分析的工具:cflow、codeviz 和 doxygen,看看效果如何。截止书稿,前两者用的的人已经越来越少了,用doxygen的比较多,从谷歌趋势图就能看出这点:


谷歌趋势图

以下是我体验过程,把他们整理出来供大家参考。

绘制函数调用图(call graph)(2):cflow + graphviz
绘制函数调用图(call graph)(3):codeviz + graphviz
绘制函数调用图(call graph)(4):doxygen + graphviz

  • 13
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用C++实现有向的邻接矩阵,以及可达矩阵的计算算法。 请完成Project05.cpp中DirectedGraph类的成员函数,具体要求如下: DirectedGraph类: 用来表示一个有向。 成员变量: m_AdjMat:邻接矩阵 m_ReachabilityMat:可达矩阵 成员函数: DirectedGraph():默认构造函数,构造一个空。 ~DirectedGraph():析构函数 DirectedGraph(string filepath):解析文件filepath,构造一个DirectedGraph对象。 filepath文件格式与项目四相同,但本项目的为有向。 DirectedGraph(const Graph & graph):复制构造函数 operator=(const Graph & graph):赋值运算符 ClearGraph():清空的邻接矩阵和可达矩阵。 OutputGraph():输出的邻接矩阵 operator*(const DirectedGraph & graph): 乘法运算符,用于实现可达矩阵运算中的矩阵逻辑乘 DirectedGraph Pow(int power):邻接矩阵的整数次幂。 用法如下: DirectedGraph a; a = a.Pow(5); 即a的5次幂,相当于a = a * a * a * a * a; 注意要考虑0次幂的情况。 该函数适合以递归实现。 DirectedGraph MatOr(DirectedGraph graph):矩阵逐元素的逻辑或运算。 例如: 1 0 0 1 与 0 1 1 0 运算后的结果为 1 1 1 1 void CalcReachabilityMat():计算可达矩阵,要求该函数基于*运算符和Pow函数实现 void OutputReachabilityMat():输出可达矩阵 IsConnected(int src, int dst):基于可达矩阵判断从节点src与dst之间是否存在通路,如存在返回真,否则返回假
05-30
以下是Project05.cpp的完整代码实现,包括DirectedGraph类的定义和成员函数的实现: ```c++ #include <iostream> #include <fstream> #include <vector> #include <cstring> using namespace std; const int MAXN = 100; // 最大节点数 class DirectedGraph { public: DirectedGraph(); DirectedGraph(string filepath); DirectedGraph(const DirectedGraph & graph); ~DirectedGraph(); DirectedGraph & operator=(const DirectedGraph & graph); void ClearGraph(); void OutputGraph(); DirectedGraph operator*(const DirectedGraph & graph); DirectedGraph Pow(int power); DirectedGraph MatOr(DirectedGraph graph); void CalcReachabilityMat(); void OutputReachabilityMat(); bool IsConnected(int src, int dst); private: int n; // 节点数 int m_AdjMat[MAXN][MAXN]; // 邻接矩阵 int m_ReachabilityMat[MAXN][MAXN]; // 可达矩阵 void ReadGraphFromFile(string filepath); // 从文件中读取 }; DirectedGraph::DirectedGraph() { n = 0; memset(m_AdjMat, 0, sizeof(m_AdjMat)); memset(m_ReachabilityMat, 0, sizeof(m_ReachabilityMat)); } DirectedGraph::DirectedGraph(string filepath) { n = 0; memset(m_AdjMat, 0, sizeof(m_AdjMat)); memset(m_ReachabilityMat, 0, sizeof(m_ReachabilityMat)); ReadGraphFromFile(filepath); } DirectedGraph::DirectedGraph(const DirectedGraph & graph) { n = graph.n; memcpy(m_AdjMat, graph.m_AdjMat, sizeof(m_AdjMat)); memcpy(m_ReachabilityMat, graph.m_ReachabilityMat, sizeof(m_ReachabilityMat)); } DirectedGraph::~DirectedGraph() {} DirectedGraph & DirectedGraph::operator=(const DirectedGraph & graph) { if (this == &graph) { return *this; } n = graph.n; memcpy(m_AdjMat, graph.m_AdjMat, sizeof(m_AdjMat)); memcpy(m_ReachabilityMat, graph.m_ReachabilityMat, sizeof(m_ReachabilityMat)); return *this; } void DirectedGraph::ClearGraph() { n = 0; memset(m_AdjMat, 0, sizeof(m_AdjMat)); memset(m_ReachabilityMat, 0, sizeof(m_ReachabilityMat)); } void DirectedGraph::OutputGraph() { cout << "邻接矩阵:" << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cout << m_AdjMat[i][j] << " "; } cout << endl; } } DirectedGraph DirectedGraph::operator*(const DirectedGraph & graph) { DirectedGraph ret; ret.n = n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { ret.m_ReachabilityMat[i][j] = ret.m_ReachabilityMat[i][j] || (m_ReachabilityMat[i][k] && graph.m_ReachabilityMat[k][j]); } } } return ret; } DirectedGraph DirectedGraph::Pow(int power) { DirectedGraph ret; ret.n = n; if (power == 0) { for (int i = 0; i < n; i++) { ret.m_ReachabilityMat[i][i] = 1; } } else if (power == 1) { memcpy(ret.m_ReachabilityMat, m_ReachabilityMat, sizeof(m_ReachabilityMat)); } else if (power % 2 == 0) { DirectedGraph half = Pow(power / 2); ret = half * half; } else { DirectedGraph half = Pow(power / 2); ret = half * half * (*this); } return ret; } DirectedGraph DirectedGraph::MatOr(DirectedGraph graph) { DirectedGraph ret; ret.n = n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { ret.m_ReachabilityMat[i][j] = m_ReachabilityMat[i][j] || graph.m_ReachabilityMat[i][j]; } } return ret; } void DirectedGraph::CalcReachabilityMat() { DirectedGraph ret; ret.n = n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { ret.m_ReachabilityMat[i][j] = m_AdjMat[i][j]; } } for (int i = 1; i < n; i++) { ret = ret + Pow(i); } memcpy(m_ReachabilityMat, ret.m_ReachabilityMat, sizeof(m_ReachabilityMat)); } void DirectedGraph::OutputReachabilityMat() { cout << "可达矩阵:" << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cout << m_ReachabilityMat[i][j] << " "; } cout << endl; } } bool DirectedGraph::IsConnected(int src, int dst) { return m_ReachabilityMat[src][dst]; } void DirectedGraph::ReadGraphFromFile(string filepath) { ifstream fin(filepath); if (!fin) { cout << "文件不存在!" << endl; return; } fin >> n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { fin >> m_AdjMat[i][j]; } } fin.close(); } int main() { DirectedGraph graph("input.txt"); graph.OutputGraph(); graph.CalcReachabilityMat(); graph.OutputReachabilityMat(); cout << "0和3是否连通:" << graph.IsConnected(0, 3) << endl; cout << "1和2是否连通:" << graph.IsConnected(1, 2) << endl; return 0; } ``` 在该代码中,我们定义了DirectedGraph类,实现了题目中要求的各种成员函数。我们首先在main函数中创建一个DirectedGraph对象,并读取输入文件中的有向。然后,我们调用OutputGraph函数输出邻接矩阵。接着,我们调用CalcReachabilityMat函数计算可达矩阵,并使用OutputReachabilityMat函数输出可达矩阵。最后,我们使用IsConnected函数判断一些节点之间是否连通。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值