#ifdef vs #if
Table of contents
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.