I'm cleaning up some C code. There are global variables everywhere, but not all of them are used. I want to clean up those. But it's too much work to test them one by one. Is there are easy way to do that?
You can generate a list of all global variables in your directory using the very helpful ctags(1)
command with the -x
command line option:
ctags -x --c-kinds=v --file-scope=no *.c
This can be combined with the also-helpful gid(1)
command (assuming you've run mkid(1)
on your sources first):
for name in `ctags -x --c-kinds=v --file-scope=no *.c | awk '{print $1;}' | sort -u` ; do gid -R filenames $name ; done
This gives you a helpful list of which files use which globals:
$ 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
...
It isn't perfect (as Ira points out), but it should be a good start.
If they are only used within the file, you can declare them "static" and GCC will warn if they are never used.
If they are used from multiple files... I'm not sure how you could detect that, other than grepping or using an IDE, because they will show up as linker symbols and could in theory be accessed by any code that links with your code...
This answer applies to the original question. The question has subsequently been changed.
It is impossible in principle to determine if a global variable is used or needed. Constructors can have side-effects, so even if a global is never accessed, it may still be needed.
True story: Programmer who shall remain nameless removed an 'unused' global from a production application. Unfortunately, the constructor for that global allocated memory from an allocator that initializes on first allocation.
As a result of him removing the unused global, the next object created using that allocator caused the allocator to initialize. Unfortunately, the initialization wasn't thread-safe (and was clearly documented as such) -- the purpose of the global was to ensure it initialized before any threads were created.
Let's just say there were very bad consequences (involving this company's largest customer -- a well-known three-letter government agency) and leave it at that.
A human being must make the determination that a global does not need to be created just because it is unused, and that can be a remarkably complicated decision to make.
Easy? No. For a global variable X, you need to scan every compilation unit in your code for a potential access to X (a read, a write, or the generation of a reference). If there are no such accesses, then you can be reasonably sure (you don't have any assembly code, right?) that X isn't used.
It may even be the case that X is referenced in one of the above ways, but in fact has no actual effect on the program (e.g., X is read but ignore, written but not read, address taken but never dereferenced). That makes X effectively dead. Determining this requires global data flow analysis.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With