Page 142 - 《软件学报》2026年第7期
P. 142
徐美秋 等: CAnalyzer: 面向 C/C++源代码的软件成分分析技术 2827
前 3 类, 仅保留核心函数构建特征. 然而, 不同 TPL 中公共或支持函数的占比差异较大, 静态阈值可能误删有价值
的函数特征. 上述方法均依赖函数诞生时间来处理公共函数归属问题, 但正如本文第 2.1 节所述, 函数诞生时间易
受版本控制工具和项目版本管理策略影响, 可能无法准确获取甚至缺失, 进而影响 SCA 检测的准确性.
相较于上述方法, 本文提出的方案避免了函数诞生时间带来的不确定性. CAnalyzer 通过明确标识信息 (如函
数路径) 过滤与特定 TPL 无关的噪声函数, 并将相似 TPL 划分为“家族”, 家族内成员共享部分公共函数, 同时保留
各自独有的标志函数. 基于被复用 TPL 中标志函数占比较低的假设, CAnalyzer 通过分析共享函数的复用关系, 更
有效地区分 TPL 自身代码与外部引入代码, 从而确保每个 TPL 特征准确反映其独有的代码结构. 该方法克服了时
间信息缺失或不准确带来的局限.
在目标代码成分识别方面, CENTRIS 和 TPLite 设定函数数量阈值, 当目标代码与 TPL 相同的函数数量达到
阈值时, 认为 TPL 是目标代码的有效成分. 而 OSSFP 则完全放弃阈值, 只要有一个函数相同即视为 TPL 存在. 尽
管它们在处理代码片段引用时表现较好, 但在软件库粒度复用检测的场景中, 容易因部分函数相同而错误地判定
TPL 的存在, 从而增加假阳性. 为了避免这一问题. CAnalyzer 采用了多重阈值策略, 若函数复用比例、文件复用比
例和库复用比例均达到预设阈值, 则确认该 TPL 为目标代码的有效成分, 以满足软件库粒度复用检测的需求.
CNEPS [54] 为 C/C++源码中引入的 TPL 生成依赖视图. 其基本流程是: 首先将目标代码划分为模块, 每个模块
由一个头文件和若干源文件组成; 然后基于成分分析结果选取主导组件作为依赖图中的节点; 最后, 通过扫描模块
中的#include 指令并与其他模块的.h 文件对比, 建立模块间的依赖关系. 然而, 该方法存在一定的局限性. 首先,
CNEPS 在遇到头文件名冲突时, 其准确性可能显著下降, 导致依赖关系识别错误. 其次, CNEPS 基于 CENTRIS 的
成分分析结果存在偏差, CNEPS 在确定模块的主导组件时, 可能会放大这些不确定性. 例如, 即使模块中仅有一个
函数与某个组件相同, CNEPS 也可能将该组件错误地标定为主导组件, 进而造成节点冗余和依赖边误判. 此外, 由
于 CNEPS 的成分分析需要在每个模块上多次执行, 效率较低, 在大规模软件项目上成本较高. 相比之下, 本文提出
的 CAnalyzer 基于一次性成分分析结果, 通过目录结构与显式元信息对 TPL 模块进行划分, 并将依赖关系建模为
文件集之间的关系, 这一方法显著降低了计算开销, 并在构建依赖图时同时考虑#include 与 extern 指令; 当存在头
文件冲突时, CAnalyzer 进一步利用标识符验证识别间接依赖, 从而避免了 CNEPS 面临的精度问题. 本文中没有
将 CAnalyzer 与 CNEPS 进行直接对比是由于实验可行性的限制, 其环境配置与输入预处理要求与我们构建的数
据集不完全兼容. 因此, 我们通过方法特性和局限性进行系统性讨论. 未来, 我们计划在统一基准条件下进一步探
索与 CNEPS 的对比, 并解决其环境配置和输入预处理与我们数据集不兼容的问题.
6.2 代码克隆检测
代码克隆检测是指在软件系统内部或不同系统之间定位相同或相似的代码片段, 这些代码片段被称为“克隆”.
克隆通常是在开发者通过复制、粘贴和修改已有代码来实现代码复用时产生的. 随着软件系统日益庞大和复杂,
代码的重复和共享成为一种常见现象. 代码克隆不仅可能导致冗余的代码库和增加维护成本, 还可能在软件安全
性、性能优化等方面带来挑战. 因此, 代码克隆检测在软件工程领域, 尤其是 SCA 中, 扮演着重要角色.
代码克隆分为 4 种类型 [55] . 基于文本相似性, 区分 Type I、Type II 和 Type III 型代码克隆. 如果两个代码片段
的功能相同或相似, 即它们具有相似的前置条件和后置条件, 则称它们为语义克隆 (Type IV). 在软件成分分析中,
我们关注的是目标代码是否包含与特定 TPL 相似或相同的源代码, 以进一步判断是否复用了该 TPL. 因此, 我们
更关注代码的结构相似性, 而非语义上的变动. 在这一过程中, 我们主要关注的是 Type I 和 Type II 型代码克隆.
目前, 有许多工具可以检测 Type I 和 Type II 类型的克隆. 其中, 基于文本的工具中, Johnson [56] 使用指纹技术
对源代码进行比较, Ducasse 等人 [57] 则使用动态模式匹配进行行级比较. NiCad [58] 是一种混合型工具, 采用最长公
共子序列算法进行代码比较. 基于令牌的工具如 SourcererCC [10] , 核心思想是将源代码转换为令牌序列, 这些令牌
通常包括关键字、标识符、操作符和分隔符等语言元素. 通过比较令牌序列的相似度, 来识别代码克隆. 此外, 基
于树和程序依赖图 (PDG) 的工具 [59,60] , 在语法和语义识别方面相较于文本方法具有更强的能力. 然而, 这些方法的
性能开销较大, 难以在大规模代码库中高效应用.

