#ifdef vs #if

Table of contents

No heading

No headings in the article.

When you have worked as a firmware engineer, you have read many different vendors' low-level driver source code which is written in C language. Also, you would see many preprocessor directives such as #if, #ifdef, #endif, #else, #error.

Vendors do not know which compiler users are going to use. Therefore, their code should be able to support multiple compilers such as gcc, IAR, Tasking. To achieve this goal, some preprocessor directives are used. The example below is code fraction of CMSIS/Include/core_cm3.h.

#if   defined ( __CC_ARM )
  #define __ASM            __asm                                      /*!< asm keyword for ARM Compiler          */
  #define __INLINE         __inline                                   /*!< inline keyword for ARM Compiler       */
  #define __STATIC_INLINE  static __inline
#elif defined ( __ICCARM__ )
  #define __ASM            __asm                                      /*!< asm keyword for IAR Compiler          */
  #define __INLINE         inline                                     /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */
  #define __STATIC_INLINE  static inline
#elif defined ( __TMS470__ )
  #define __ASM            __asm                                      /*!< asm keyword for TI CCS Compiler       */
  #define __STATIC_INLINE  static inline
#elif defined ( __GNUC__ )
  #define __ASM            __asm                                      /*!< asm keyword for GNU Compiler          */
  #define __INLINE         inline                                     /*!< inline keyword for GNU Compiler       */
  #define __STATIC_INLINE  static inline
#elif defined ( __TASKING__ )
  #define __ASM            __asm                                      /*!< asm keyword for TASKING Compiler      */
  #define __INLINE         inline                                     /*!< inline keyword for TASKING Compiler   */
  #define __STATIC_INLINE  static inline
#endif

In the same manner, using preprocessor directives can support various operating systems as well. For example,

#ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */
# include <unistd.h>
#elif defined _WIN32 /* _WIN32 is usually defined by compilers targeting 32 or 64 bit Windows systems */
# include <windows.h>
#endif

Sometimes, when you work on a project, you would like to exclude debugging code in the production version. Typically, this can be achieved by each project including a different symbol. For example, in debug build, we would like to print some information but not in the production.

debug build - the project should define a symbol DEBUG in the project property

production build - the project defines a symbol NDEBUG in the project property

uint32_t add(uint32_t value1, uint32_t value2)
{
    uint32_t result = value1 + value2;
#ifdef DEBUG
    printf("%d\n", result);
#endif
    return result;
}

When add function is used in the production version, printf line will be commented out.

Finally, back to the original question which is the title of this post! What is the difference between #ifdef and #if?

The answer is here if you Google it and comes out right away: #if checks for the value of the symbol, while #ifdef checks the existence of the symbol. Another difference is that #if can express multiple conditions by using a logic operator such as && and ||.

For example,

uint32_t add(uint32_t value1, uint32_t value2)
{
    uint32_t result = value1 + value2;
#if defined(DEBUG) && !defined(NDEBUG)
    printf("%d\n", result);
#endif
    return result;
}

What happened if you are using #if instead of #ifdef in add function? Will it be compiled?

uint32_t add(uint32_t value1, uint32_t value2)
{
    uint32_t result = value1 + value2;
#if DEBUG
    printf("%d\n", result);
#endif
    return result;
}

If you want to print a result on the console, It will depend on how you define DEBUG symbol in the project property. To use #if, it should be defined as DEBUG=1, not just DEBUG. However, any values are acceptable except 0. If it is defined as just DEBUG, it still compiles fine but printf line is not executable.

Did you find this article valuable?

Support Hyunwoo Choi by becoming a sponsor. Any amount is appreciated!