"[C] A Bug Caused by Misuse of '#if' Macro"

Bug description

To fully simplify my situation but also clearly show the key points, I use some dummy code to describe this bug.

Considering that we want to let our code execute in following logic:

But to our surprise, when 'FOO' is 'not defined', program still executes in path A.

Original code

FOO is defined:

#include <stdio.h>
#include <stdlib.h>

#define FOO true

int main(int argc, char **argv)
{
#if FOO == true
    printf("I'm foo.\n");
#else
    printf("I'm not foo.\n");
#endif

    return 0;
}

Compile and Run:

$ gcc misuse_of_if_macro.c ; ./a.out
I'm foo.
#=> this is what we expect

FOO is not defined:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
#if FOO == true
    printf("I'm foo.\n");
#else
    printf("I'm not foo.\n");
#endif

    return 0;
}

Compile and Run:

$ gcc misuse_of_if_macro.c ; ./a.out
I'm foo.
#=> this is not what we expect

Replace 'true' with '1'

FOO is defined

#include <stdio.h>
#include <stdlib.h>

#define FOO 1

int main(int argc, char **argv)
{
#if FOO == 1
    printf("I'm foo.\n");
#else
    printf("I'm not foo.\n");
#endif

    return 0;
}
$ gcc misuse_of_if_macro.c ; ./a.out
I'm foo.
#=> this is what we expect

FOO is not defined

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
#if FOO == 1
    printf("I'm foo.\n");
#else
    printf("I'm not foo.\n");
#endif

    return 0;
}
$ gcc misuse_of_if_macro.c ; ./a.out
I'm not foo.
#=> this is what we expect

Cause of bug

According to GCC document which describes #if macro:

#if expression

controlled text

#endif /* expression */

In our situation, when 'FOO' is not defined, 'FOO' in '#if FOO == true' is treated as 0. On the other hand, based on the last rule that the GCC document describes, 'true' is also treated by GCC preprocessor as 0. Therefore, when 'FOO' is not defined, the program goes to the wrong branch.

When we replace 'true' with '1', #if is now evaluating a arithmetic expression. So we can get what we want.

In summary, #if macro expects an arithmetic expression, in which a string like 'true' is not allowed.

Solution

In this case, we just need to use #ifdef instead of #if since the value inside 'FOO' is not concerned at all.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
#ifdef FOO
    printf("I'm foo.\n");
#else
    printf("I'm not foo.\n");
#endif

    return 0;
}

References

[1] https://gcc.gnu.org/onlinedocs/cpp/If.html