Using C Defines to Change Constants During Compilation

Repeatedly Editing Code

Today, let’s dive right into an annoyance we’ve all experienced: needing to edit our code repeatedly to modify a constant. Consider the following snippet of C code used to run a scientific experiment with multiple trials:

#include <stdio.h>

#define NUM_TRIALS 100

int main(void)
{
    printf("Starting %d trials\n", NUM_TRIALS);
    for (int i = 0; i < NUM_TRIALS; i++) {
        ;  // Trials go here
    }
    return 0;
}

Let’s compile this code:

% cc -Wall -Wextra -Wpedantic -std=c99 -o experiment experiment.c

And when we run it, we see that there are 100 trials in our experiment:

% ./experiment
Starting 100 trials

But if we want to change the number of trials, we have to open experiment.c to edit the constant, then recompile. What a pain.

Potential Alternate Approaches

There are numerous ways to address this annoyance. For example, our program could accept a command-line argument at run-time, to change the number of trials from the default of 100 to another value:

% ./experiment -t 200
Starting 200 trials

Alternately, we could introduce a configuration file, experiment.conf, which contains that value:

trials 200

But adding command-line argument processing can be overkill for a small program, and introducing a configuration file even more so.

What if we could, instead, define the constant’s value on the command line at compile-time? Something like:

% cc [...something to set NUM_TRIALS to 200...] -o experiment experiment.c

Then, when we run the program, we run the 200 trials chosen during compilation:

% ./experiment
Starting 200 trials

Setting a Default and Compiling

To change the value of our constant at compile-time, we first have to make a small modification to our C code. Our code should continue to run 100 trials, but only if we don’t explicitly set a different value at compile-time. The #ifndef preprocessor directive provides this functionality (it’s described in Section 6.8.1 of the C89 standard, or Section 6.10.1 of the C99 standard). We can use it so that NUM_TRIALS gets defined to a default value of 100, but only if it hasn’t already been defined elsewhere:

#include <stdio.h>

#ifndef NUM_TRIALS
#define NUM_TRIALS 100
#endif

int main(void)
{
    printf("Starting %d trials\n", NUM_TRIALS);
    for (int i = 0; i < NUM_TRIALS; i++) {
        [...]
    }
    return 0;
}

If we don’t define NUM_TRIALS elsewhere, the code still runs 100 trials:

% cc -Wall -Wextra -Wpedantic -std=c99 -o experiment experiment.c
% ./experiment
Starting 100 trials

But when we compile our code, we can use the -D compiler flag to define NUM_TRIALS as a different value:

% cc -DNUM_TRIALS=200 -Wall -Wextra -Wpedantic -std=c99 -o experiment experiment.c

Because we’ve defined NUM_TRIALS, the code inside the #ifndef directive (that would have set NUM_TRIALS to 100) doesn’t execute. Instead, the value defined on the command line is used. So, without having to make any edits to experiment.c, we’ve changed the number of trials to 200:

% ./experiment
Starting 200 trials

By reducing the number of times we need to edit our source code, we save time and reduce the risk of accidentally introducing an error into our code.

For more tips, and to arrange for personalized tutoring for yourself or your study group, check out Vancouver Computer Science Tutoring.