像向量和填充,然后去未使用,定义但从未使用过的类/结构,以及声明但从未使用过的函数.
我知道在许多情况下,其中一些不是多余的,因为它们可能从其他文件中可见,但在我的情况下,没有其他文件,只是我文件中的无关代码.
虽然我从技术上讲理解,调用push_back会有所作为,因此向量本身并不是未使用的,在我的例子中,它的结果是未使用的.
那么:有没有办法做到这一点,使用编译器(clang,gcc,VS等)或外部工具?
例:
#include<vector> using namespace std; void test() { vector<int> a; a.push_back(1); } int main() { test(); return 0; }
应该成为:int main(){return 0};
解决方法
通常,您需要一个静态分析来确定是否使用每个计算(结果,可能有几个,只考虑“x”).对于每个未使用的计算,实际上您要删除未使用的计算,并重复分析.出于效率原因,您希望进行一次分析,以确定结果的所有(点)使用次数;这本质上是一种数据流分析.当计算结果的使用集变空时,可以删除该计算结果(请注意,删除“x”值结果可能会留下“x”因为仍然需要增量!)以及它所依赖的计算的使用集合可以调整以删除已删除的引用,可能导致更多删除.
要对任何语言进行此分析,您必须能够跟踪结果.对于C(和C)来说,这可能非常丑陋;存在“明显的”用法,其中计算结果用于表达式,并且它被分配给局部/全局变量(在其他地方使用),并且通过指针,对象字段更新,通过任意强制转换进行间接赋值要了解这些影响,您的死代码分析工具必须能够读取整个软件系统,并计算整个软件系统的数据流.
为了安全起见,您希望分析是保守的,例如,如果工具没有证明结果未被使用,那么它必须假设结果被使用;你经常不得不用指针(或数组索引只是伪装的指针)来做这件事,因为一般来说你无法准确确定指针“指向”的位置.通过假设使用所有结果,显然可以建立一个“安全”工具: – }对于没有源的库例程,您最终会得到有时非常保守但必要的假设.在这种情况下,有一组库计算副作用的预先计算摘要是有帮助的(例如,“strcmp”没有,“sprintf”覆盖特定的操作数,“push_back”修改其对象…).由于库可能非常大,因此这个列表可能非常大.
DMS一般可以解析和整个源代码库,构建符号表(因此它知道哪些标识符是本地/全局及其精确类型),进行控制和本地数据流分析,为每个函数构建本地“sideeffects”摘要,构建调用图形和全局副作用,并进行全局点分析,为这种“计算使用”信息提供适当的保守性.
DMS已被用于在2600万行代码的C代码系统上进行这种计算(是的,这是一个非常大的计算;它需要运行100Gb的VM).我们没有实现死代码消除部分(该项目有另一个目的)但是一旦你有了这些数据,那就很简单了. DMS已经使用更保守的分析(例如,“没有使用标识符的提及”意味着对标识符的分配已经死亡)对大型Java代码执行死代码消除,这导致许多实际代码中的代码移除量惊人.
DMS的C解析器目前构建符号表,并且可以控制C 98的流量分析,C 11就在眼前.我们仍然需要本地数据流分析,这是一些努力,但全局分析已经预先存在于DMS中,可用于此效果. (如果您不介意更保守的分析,则可以从符号表数据中轻松获得“不使用标识符”).
实际上,你不希望这个工具只是默默地扯掉东西;有些可能实际上是你希望保留的计算. Java工具所做的是产生两个结果:一个死计算列表,您可以检查它以确定您是否相信它,以及一个死代码删除的源代码版本.如果您相信死代码报告,则保留死代码删除版本;如果你看到一个“死”的计算你认为不应该死,你修改代码使其不死并再次运行该工具.有了很大的代码库,检查死代码报告本身就可以尝试; “你”如何知道你的团队中“某人”不重视某些特定的死代码? (版本控制可用于恢复,如果你蠢!)
我们没有(也没有我知道的工具)处理的一个非常棘手的问题是在有条件编译的情况下是“死代码”. (Java没有这个问题; C在黑桃中使用它,C系统更少).这真的很讨厌.想象一下,一个条件,其中手臂有一定的副作用,另一个手臂有不同的副作用,或另一种情况,其中一个由GCC的C编译器解释,另一个手臂由MS解释,编译器不同意结构的作用(是的,C编译器在黑暗的角落里不同意).充其量我们在这里可以非常保守.
CLANG有能力进行流量分析;以及一些进行源转换的能力,因此可能会强制执行此操作.我不知道它是否可以进行任何全局流量/点分析.它似乎偏向于单个编译单元,因为它的主要用途是编译单个编译单元.