我正在整理一些C语言代码。到处都是全局变量,但并非所有全局变量都被使用。我想清理这些变量。但一个一个测试它们太麻烦了。有没有简单的方法可以做到这一点?
使用非常有用的ctags(1)
命令和-x
命令行选项,您可以生成目录中所有全局变量的列表:
ctags -x --c-kinds=v --file-scope=no *.c
这可以与同样有帮助的gid(1)
命令结合使用(假设您先在源代码上运行了mkid(1)
):
for name in `ctags -x --c-kinds=v --file-scope=no *.c | awk '{print $1;}' | sort -u` ; do gid -R filenames $name ; done
$ for name in `ctags -x --c-kinds=v --file-scope=no *.c | awk '{print $1;}' | sort -u` ; do gid -R filenames $name ; done
basedir parser.h ./{parser_include,parser_main}.c
binary_input parser_main.c
cache_fd parser.h ./{parser_interface,parser_main}.c
conf_quiet parser.h parser_main.c
conf_verbose parser.h ./{parser_interface,parser_main}.c
...
虽然它并不完美(如Ira所指出的),但这应该是一个很好的开始。
这个答案适用于原始问题。该问题随后已更改。
从原理上讲,无法确定全局变量是否被使用或需要。构造函数可能具有副作用,因此即使从未访问全局变量,它仍然可能是必要的。
真实故事:某程序员从生产应用程序中删除了一个“未使用”的全局变量。不幸的是,该全局变量的构造函数从分配器中分配内存,该分配器在首次分配时进行初始化。
由于他删除了未使用的全局变量,因此使用该分配器创建的下一个对象导致分配器初始化。不幸的是,该初始化不是线程安全的(并且明确记录为这种情况) - 全局变量的目的是确保在创建任何线程之前初始化它。
我们只能说,发生了非常糟糕的后果(涉及该公司最大的客户 - 一个著名的三字母政府机构),就到此为止吧。
必须由人类决定是否需要创建全局变量,而这可能是一个非常复杂的决定。
简单吗?不是的。对于一个全局变量X,你需要扫描代码中的每个编译单元,以寻找对X的潜在访问(读取、写入或生成引用)。如果没有这样的访问,则可以合理地确定(没有汇编代码,对吧?)X未被使用。
甚至可能出现以上某种方式引用了X,但实际上对程序没有任何实际影响的情况(例如,读取了X但被忽略,写入了X但未被读取,地址被获取但从未被解引用)。 这使得X实际上已经死亡。 确定这一点需要进行全局数据流分析。
(@ChrisLutz在评论中提出了这个想法,但我在阅读评论之前就想到了它,所以我还是要发表一下。我会尝试通过扩展来增加一些价值。)
在源代码的另一个副本中,注释掉所有全局变量的声明并重新编译所有内容。将您注释掉的变量列表与编译器错误消息中提到的变量进行比较。任何未在错误消息中提到的内容都是未使用的。
请注意,您的构建过程可能不会尝试构建所有内容,因此可能会导致结果不完整。
稍微不同的方法是迭代方法:注释掉所有全局变量,尝试构建,然后取消注释编译器抱怨的任何声明。重复此过程,直到获得干净的构建,并查看仍被注释的内容。
extern
,省略初始化可能会减少需要处理的错误信息。此外,我想知道是否有任何警告开关(如果不是为编译器,则为链接器)可以提醒未使用的局部变量和全局变量。 - Alexey Frunze