Using predefined compiler macros in C and C++

Published on 2021-12-10. Modified on 2023-05-30.

C and C++ compilers provide a set of predefined macros which can be used to detect a wide variety of build settings and target properties at compile time. Combining them with the preprocessor allows for platform-specific code to be guarded at compile time.

Listing predefined macros

To list predefined compiler macros, we can use the following commands:

$ clang -dM -E -x c /dev/null
$ clang++ -dM -E -x c++ /dev/null

Options:

Note: clang (resp. clang++) can be replaced by gcc (resp. g++).

Detecting the target operating system

We can used predefined compiler macros to detect the target operating system at compile time when working with C and C++ code. This can allow us to conditionally enable or disable parts of the code depending on the target, which is useful when writing cross-platform code.

The following code snippet detects common target operating systems:

#if defined(__linux__)
/* Linux ...........................................................*/
#elif defined(__APPLE__) || defined(__MACH__)
/* Mac OSX and Darwin (iOS) ........................................*/
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR == 1
/* iOS in XCode simulator */
#elif TARGET_OS_IPHONE == 1
/* iOS on iPhone, iPad, etc. */
#elif TARGET_OS_MAC == 1
/* Mac OSX */
#endif
#elif defined(__CYGWIN__) && !defined(_WIN32)
/* POSIX Cygwin under Microsoft Windows ............................*/
#elif defined(_WIN32)
/* 32-bit Microsoft Windows ........................................*/
#elif defined(_WIN64)
/* 64-bit Microsoft Windows ........................................*/
#endif

Detecting the target processor architecture

In addition to operating system detection, predefined compiler macros can be used to determine the target architecture for which our code is compiled. In particular, we can detect x86 and AMD64 platforms using the following predefined macros:
#if defined(__x86_64__) || defined(_M_X64)
/* AMD64 / x86-64 ..................................................*/
#elif defined(__i386) || defined(_M_IX86)
/* x86 .............................................................*/
#endif

Note: The _M_X64 and _M_IX86 variants are defined by the Microsoft Visual Studio compiler.

Detecting the compiler name and version

We can also leverage predefined compiler macros to detect the compiler name and version of any major C and C++ compiler at compile time.

Compiler name

The following snippet checks for the three major C and C++ compilers currently used today.

#if defined(__clang__)
/* Clang/LLVM ......................................................*/
#elif defined(__GNUC__) || defined(__GNUG__)
/* GNU GCC/G++ .....................................................*/
#elif defined(_MSC_VER)
/* Microsoft Visual Studio .........................................*/
#endif
Warning! Even though __GNUC__ and __GNUG__ were first intended to identify the GNU compiler suite, Clang and some other compilers also define them to indicate their compatibility with GNU behavior. To explicitly check for gcc/g++, use
#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__)
/* GNU GCC/G++ .....................................................*/
#endif

Compiler version

Compiler versions are encoded in a variety of ways that are not always compatible with one another.

See also

The following links point to resources that were once available online but are only accessible to the Internet Archive today. They list additional predefined macros for less common compilers and target platforms.