DEV Community

Martin Licht
Martin Licht

Posted on

Unused variables in C/C++: why and how?

When reviewing C/C++ code, we fairly often find variables that are never used. Such unused variables occur for a variety of reasons. In this post, we look at some of the most common causes why unused variables occur in C/C++ code. We review how to have the compiler warn about unused variables, and how to suppress warnings for specific unused variables.

Why variables are unused

There are numerous reasons why unused variables may remain in a code base. These include:

  1. Bugs and mistakes: the most obvious reason for an unused variable is flawed code. Either the variable really isn't needed at all and can be removed, or the variable is necessary but we have forgotten to use it at some critical points.

  2. Refactoring: As software is written and re-written, entire sections of code may be removed. Variables that were once vital to the code, such as results of auxiliary calculations, may then be left behind, unused.

  3. Future-proofing: unused variables might not only occur as a legacy of past code but also as legacy of future code. You might declare variables in anticipation of code that is yet to be written.

  4. Conditional compilation: variables may remain unused depending on the preprocessor phase. A standard example is a variable that is only defined for debug purposes. Your code may contain something of the form

const auto value = compute_some_value();
const auto value_for_comparison_only = compute_same_value_differently();
assert( value == value_for_comparison_only );
Enter fullscreen mode Exit fullscreen mode

If you compile with -DNDEBUG, then compiler might warn you that value_for_comparison_only is never used: indeed, the assert statement has been replaced with … nothing.

How to detect unused variables

Different compilers and warning level settings can influence whether a variable is reported as unused during the compilation process.

For instance, GCC and Clang feature the -Wunused-variable flag, which triggers warnings about unused variables. The flag is already implied by the -Wall warning option, and it can be turned off via -Wno-unused-variable.

My recommendation is to compile always with -Wall and then selectively turn off warnings as permissible. This will all instances of unused variables.

How to not detect unused variables: attributes

While we should always compile with as many warnings enabled as feasible, there are circumstances where we want to selectively turn off warnings about particular unused variables. A popular way of doing so is a cast to void:

Object unused_object;
(void)unused_object;
Enter fullscreen mode Exit fullscreen mode

The cast to void counts as a (pro forma) usage of the variable, so no warning will be emitted.

While this removes the warnings that unused_object is unused, just as intended, there are ways to improve this. We would like to have explicit semantics that this void-cast signifies an unused object. A common way is via defining a macro:

#define UNUSED(x) (void)(x);
// ...
Object unused_object;
UNUSED(unused_object);
Enter fullscreen mode Exit fullscreen mode

One advantage is that we now explicitly communicate the intent (or lack thereof) of this variable. Moreover, if we decide to purge the code of unused variables, then searching them is much easier.

Going beyond macros, we have variable attributes: either native to the language C++ or as language extension provided by the C/C++ compilers. For example, Clang and GCC allow for the variable attribute __attribute__((unused)). C++17 supports the [[maybe_unused]] attribute:

Object unused_object2 __attribute__((unused)) = x;  // should be placed after declaration
[[maybe_unused]] Object unused_object1 = x;         // must be placed before declaration
Enter fullscreen mode Exit fullscreen mode

These attributes communicate to the compiler (and to us) that these variables might be unused and that we're just fine with that.

Historically, the GCC attributes have appeared first, being compiler-specific language extensions in C and C++. From C++17 onwards, attributes are part of the language standard. However, not only is the spelling different, but the standard and the GCC extensions don't agree where to place the attribute.

The [[maybe_unused]] attribute will find most of its applications with conditional compilation. For example, it is a natural attribute for debug-only variables. For purely aesthetic reasons, I personally prefer to define a macro #define MAYBE_UNUSED [[maybe_unused]] .

An advantage of __attribute__((unused)) is that it will actually warn you if the variable is ever used in the code. It’s not maybe unused but definitely not used ever, and using the variable will now produce a warning.

Keeping useless things

Apparently, unused variables are frequent and important enough to even warrant their own language extensions.

By comparison, is commenting out the unused variable a good strategy? Not always! There are reasons why you would keep an unused variable around throughout the development code and the debugging stage. Suppose the variable was used in a past version of the code, maybe you haven’t even decided yet whether the old code should be shelved or re-integrated; meaning: you don’t know whether you might need the unused variable ever again.

It might helpful for debugging purposes to keep code like:

auto unused_variable __attribute__((unused)) = complicated_calculation( arg1, arg2, arg3 );
Enter fullscreen mode Exit fullscreen mode

Even if the result of the complicated calculation is never used, keeping it around constitutes another point of failure … and that is exactly what you want during debugging. Even if not originally intended for debugging, making the program initialize this variable will be helpful if you ever decide that you need it (again).

I hope this posting about unused variables was useful for you.

Top comments (0)