一个函数看起来只有几处分支,Understand给出的复杂度却比手工统计高;文件里的函数改短了,汇总数值又没有明显下降。遇到这类差异,可以先确认双方统计的是否为同一种指标,以及数据库有没有按当前代码重新分析。复杂度还会受到宏展开、条件编译和统计对象的影响,单凭源码中出现了几个判断语句,未必能够直接核对结果。以下以C/C++工程为例,采用Understand英文界面的菜单名称。
一、Understand如何统计代码复杂度指标
Understand既提供函数级复杂度,也提供文件、类等对象的汇总值。开始统计前,应确定准备观察的是某个函数的控制流,还是一组函数的整体变化。
1、完成分析并打开指标浏览器
指标来自分析数据库。源码已经修改,但数据库没有更新时,编辑器显示的代码与指标结果就可能不对应。
①打开目标工程,点击【Project】→【Analyze All Files】,等待分析完成。
②点击【Metrics】→【Browse Metrics】,打开指标浏览器。
③在左侧选择目标文件,再定位到需要检查的函数或方法。
④记录当前对象的名称、定义文件及【Cyclomatic】数值。
2、选择复杂度指标及其变体
①点击【Project】→【Configure Project】,进入【Metrics】→【Complexity】。
②按需要启用【Show all available cyclomatic complexity metric variants】,确认配置。
③返回【Browse Metrics】,分别查看【Cyclomatic】、【CyclomaticStrict】和【CyclomaticModified】。
普通圈复杂度、严格圈复杂度和修改圈复杂度使用不同口径。严格口径会额外考虑条件表达式中的逻辑与、逻辑或,修改口径则把整个多路选择结构按一项处理。比较不同工具或历史报告时,应保留具体指标名称,不能只写“复杂度”。
3、保存函数明细和汇总结果
①在指标浏览器中选中需要导出的文件、类或架构范围。
②点击【Metrics】→【Export Metrics】,选择【Export to CSV】,设置导出对象及指标。
③同时保留函数级【Cyclomatic】和适用对象的【SumCyclomatic】、【AvgCyclomatic】、【MaxCyclomatic】,保存结果。
二、Understand复杂度指标与实际代码不符如何复核
数值看起来不合理时,可以先选一个函数单独检查。直接对着整个项目总分找原因,容易把统计范围变化和函数内部变化混在一起。
1、核对手工计算采用的规则
对于常规的单入口、单出口结构,圈复杂度可以按判定点数量加一理解。但判定点不只包含普通条件判断,循环、条件运算等结构也应按当前语言和指标定义核对。
①在【Browse Metrics】中选中异常函数,点击【Metrics Definitions】,查看对应指标说明。
②对照源码中的【if】、【for】、【while】及条件运算符,记录参与计算的结构。
③源码含【switch】时,分别比较【Cyclomatic】与【CyclomaticModified】,检查多路选择的处理差异。
④条件包含【&&】或【||】时,比较【Cyclomatic】与【CyclomaticStrict】。
2、检查宏和条件编译改变了哪些代码
①打开【Project】→【Configure Project】,检查【C++】下的包含目录、宏定义和编译器相关设置。
②对照实际构建参数,核对【#if】、【#ifdef】控制的分支是否符合当前目标配置。
③找到函数中使用的宏,查看宏定义内部是否包含【if】、【while】或【for】等结构。
④修改配置后,点击【Project】→【Analyze All Files】,重新读取指标。
宏调用在源码中可能只占一行,展开后却带有分支或循环,复杂度会受到这些结构影响。相反,条件编译关闭的代码通常不会按当前有效代码参与解析。复核时应对应具体构建配置,不能把屏幕上能看到的所有分支一并加进去。
3、排除解析异常和统计对象混用
头文件缺失、语言设置不匹配,可能影响函数识别及控制流分析。函数数值与文件汇总值看似矛盾时,还应核对是否选中了声明、定义或不同层级的对象。
①点击【Project】→【Improve Project Accuracy】→【Missing Includes】,检查缺失头文件并补充目录。
②打开【Undefined Macros】,核对确实需要配置的宏,按实际构建环境补充。
③点击【Project】→【Analyze All Files】,重新检查分析提示。
④返回【Browse Metrics】,核对函数定义文件,再分别查看函数值与文件级【SumCyclomatic】。
三、Understand复杂度复核后如何比较代码变化
确认计算口径和解析配置之后,再讨论数值是否反映了代码变化。文件总量、单个复杂函数和嵌套深度,适合回答的问题并不相同。
1、用控制流图核对具体差异
①在源码编辑器中右击目标函数内的代码,选择【Graphical Views】→【Control Flow】。
②对照图中的分支与循环,定位手工统计时遗漏的结构。
③右击相关图形框,选择【Go To Source】,返回对应源码。
④在【Browse Metrics】中同时查看【MaxNesting】与圈复杂度,记录两项指标的变化。
控制流图用于帮助理解结构,不能简单数图形框得到圈复杂度。函数经过整理后,嵌套可能变浅,但分支数量并未减少,这时嵌套深度下降、圈复杂度保持不变也说得通。指标之间出现不同变化,应结合代码判断。
2、在相同统计范围下比较前后版本
把一个函数拆成多个小函数后,单个函数的复杂度可能下降,文件的复杂度合计却未必同步下降。每个新增函数都有自身的基础计数,汇总时会反映出来。
①为修改前后版本分别保留分析工程,核对【Configure Project】中的语言、宏和指标设置。
②使用【Export Metrics】导出相同范围的函数明细,按定义文件和函数名称对应比较。
③对照【MaxCyclomatic】、【AvgCyclomatic】及【SumCyclomatic】,核对函数数量变化。
④将生成代码、第三方代码等范围调整单独记录,避免与业务代码修改混合解释。
总结
Understand的复杂度指标能够为代码检查和维护安排提供线索,帮助团队发现值得花时间阅读的函数。数值反映的是特定规则下的代码结构,不能直接代替对业务难度、可读性或缺陷风险的判断。保持分析配置与统计范围一致,再结合控制流和修改记录解释变化,结果才更便于交流和复查。希望这些方法能帮助大家核清指标差异,把分析结果用到具体维护工作中。如有Understand相关需求,欢迎联系咨询。