Search code examples
cmacrosc-preprocessorpreprocessor-directive

Is it correct to say if any non-macro variable that is used in a conditional directive evaluates to 0?


Let's say I have the following piece of code. Is it correct to say that the conditional directive always evaluates to zero since M is expanded before compile time where the value of i is zero according to C specification? Could someone explain it if I'm wrong:

#include <stdio.h>

#define M i

int main() {
    int i = 10;
    #if M == 0
     i = 0;
    #endif
    printf("%d", i);
    return 0;
}

Solution

  • With #if identifiers that are not macros are all considered to be the number zero. The gcc docs for #if lay out the requirements nicel, which says (emphasis mine going forward):

    #if expression
    

    expression is a C expression of integer type, subject to stringent restrictions. It may contain

    • Integer constants.
    • Character constants, which are interpreted as they would be in normal code.
    • Arithmetic operators for addition, subtraction, multiplication, division, bitwise operations, shifts, comparisons, and logical operations (&& and ||). The latter two obey the usual short-circuiting rules of standard C.
    • Macros. All macros in the expression are expanded before actual computation of the expression's value begins.
    • Uses of the defined operator, which lets you check whether macros are defined in the middle of an ‘#if’.
    • Identifiers that are not macros, which are all considered to be the number zero. This allows you to write #if MACRO instead of #ifdef MACRO, if you know that MACRO, when defined, will always have a nonzero value. Function-like macros used without their function call parentheses are also treated as zero.

    and notes that:

    In some contexts this shortcut is undesirable. The -Wundef option causes GCC to warn whenever it encounters an identifier which is not a macro in an ‘#if’.

    This is covered in the draft C99 standard section 6.10.1 Conditional inclusion paragraph 4 which says:

    [...]After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0, and then each preprocessing token is converted into a token.[...]