在C项目中,有没有一种方法可以识别所有未使用的全局变量?

8

我正在整理一些C语言代码。到处都是全局变量,但并非所有全局变量都被使用。我想清理这些变量。但一个一个测试它们太麻烦了。有没有简单的方法可以做到这一点?


1
大多数编译器会为这种情况生成警告。尝试将警告级别调高到最大。 - VoidStar
1
VoidStar:编译器如何为全局变量执行此操作?它一次只能看到一个编译单元。 - Ira Baxter
将变量声明/定义注释掉,看看是否会出现错误? - Chris Lutz
6个回答

7

使用非常有用的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所指出的),但这应该是一个很好的开始。


3
如果它们只在文件内使用,您可以将它们声明为“static”,GCC会警告它们是否从未被使用。如果它们在多个文件中使用…我不确定如何检测它们,除了使用grep或IDE,因为它们会显示为链接器符号,并且理论上可以被任何链接到您的代码的代码访问...

3

这个答案适用于原始问题。该问题随后已更改。

从原理上讲,无法确定全局变量是否被使用或需要。构造函数可能具有副作用,因此即使从未访问全局变量,它仍然可能是必要的。

真实故事:某程序员从生产应用程序中删除了一个“未使用”的全局变量。不幸的是,该全局变量的构造函数从分配器中分配内存,该分配器在首次分配时进行初始化。

由于他删除了未使用的全局变量,因此使用该分配器创建的下一个对象导致分配器初始化。不幸的是,该初始化不是线程安全的(并且明确记录为这种情况) - 全局变量的目的是确保在创建任何线程之前初始化它。

我们只能说,发生了非常糟糕的后果(涉及该公司最大的客户 - 一个著名的三字母政府机构),就到此为止吧。

必须由人类决定是否需要创建全局变量,而这可能是一个非常复杂的决定。


啊,是的,只写变量的乐趣。或者是内存映射的只写I/O位置。 - Phil Lello
1
这个问题被标记为 [tag:c]。什么是“构造函数”? - Keith Thompson
Keith Thompson:OP说“我正在清理一些C/C++代码”。同意,他的标签只写了“C”。 - Ira Baxter
我同意。我之前把它说成是C/C++项目,让事情变得太复杂了。我已经更改了标题。 - TerryYin

2

简单吗?不是的。对于一个全局变量X,你需要扫描代码中的每个编译单元,以寻找对X的潜在访问(读取、写入或生成引用)。如果没有这样的访问,则可以合理地确定(没有汇编代码,对吧?)X未被使用。

甚至可能出现以上某种方式引用了X,但实际上对程序没有任何实际影响的情况(例如,读取了X但被忽略,写入了X但未被读取,地址被获取但从未被解引用)。 这使得X实际上已经死亡。 确定这一点需要进行全局数据流分析。


1

(@ChrisLutz在评论中提出了这个想法,但我在阅读评论之前就想到了它,所以我还是要发表一下。我会尝试通过扩展来增加一些价值。)

在源代码的另一个副本中,注释掉所有全局变量的声明并重新编译所有内容。将您注释掉的变量列表与编译器错误消息中提到的变量进行比较。任何未在错误消息中提到的内容都是未使用的。

请注意,您的构建过程可能不会尝试构建所有内容,因此可能会导致结果不完整。

稍微不同的方法是迭代方法:注释掉所有全局变量,尝试构建,然后取消注释编译器抱怨的任何声明。重复此过程,直到获得干净的构建,并查看仍被注释的内容。


在程序中添加 extern,省略初始化可能会减少需要处理的错误信息。此外,我想知道是否有任何警告开关(如果不是为编译器,则为链接器)可以提醒未使用的局部变量和全局变量。 - Alexey Frunze

0

Doxygen 可能能够为您提供全局变量的用途列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接