Use this file to discover all available pages before exploring further.
A macro is a name that the preprocessor replaces with a specified token sequence before the compiler ever sees the source file. Macros are defined with #define and removed with #undef. The preprocessor expands macros in all lines except preprocessor directive lines — those that have # as the first non-whitespace character. Understanding macro expansion rules, operator interactions, and the pitfalls of incorrect macro design is essential for writing robust, portable C and C++ code with MSVC.
A function-like macro accepts one or more formal parameters, which are textually substituted into the replacement list whenever the macro is invoked. The opening parenthesis must immediately follow the macro name (no space).
/* Basic arithmetic macros */#define MAX(a, b) ((a) > (b) ? (a) : (b))#define MIN(a, b) ((a) < (b) ? (a) : (b))#define ABS(x) ((x) < 0 ? -(x) : (x))#define CLAMP(v,lo,hi) ((v) < (lo) ? (lo) : (v) > (hi) ? (hi) : (v))/* Macro that generates a range cursor */#define CURSOR(top, bottom) (((top) << 8) | (bottom))/* Macro to compute a random integer in [min, max] */#define GETRANDOM(min, max) \ ((rand() % (int)(((max) + 1) - (min))) + (min))
If a macro parameter appears more than once in the token string, any argument expression with side effects is evaluated more than once:
#define MAX(a, b) ((a) > (b) ? (a) : (b))int i = 5;int m = MAX(i++, 3);/* expands to: ((i++) > (3) ? (i++) : (3)) i is incremented TWICE — undefined behavior */
Never pass expressions with side effects (like i++, ++i, function calls that modify state) to function-like macros that use the parameter more than once. Prefer inline functions or static inline functions when side effects are possible.
The MSVC preprocessor follows these rules when expanding a macro call:
Argument pre-scanning: Each actual argument is fully macro-expanded before substitution into the replacement list, unless the parameter is preceded by #, followed by ##, or immediately preceded by ##.
Formal parameter substitution: Each occurrence of a formal parameter in the replacement list is replaced by the (possibly pre-expanded) actual argument.
Rescan: The expanded result is rescanned for further macro calls.
#define STRINGIFY(x) #x#define EXPAND(x) STRINGIFY(x)#define MY_VALUE 42STRINGIFY(MY_VALUE) /* → "MY_VALUE" (no pre-scan because # suppresses it) */EXPAND(MY_VALUE) /* → "42" (pre-scan expands MY_VALUE to 42 first) */
When a formal parameter is preceded by # in the replacement list, the preprocessor converts the actual argument into a string literal. The result is surrounded by double quotes, and any embedded quotes or backslashes are escaped.
The ## operator concatenates two tokens in the replacement list, forming a new single token. The operands are not macro-expanded before pasting; use an extra level of indirection if you need expansion first.
/* Build field names at compile time */#define FIELD(prefix, num) prefix##numstruct Point { int FIELD(coord, 0); /* coord0 */ int FIELD(coord, 1); /* coord1 */ int FIELD(coord, 2); /* coord2 */};/* Generate unique variable names (common with __COUNTER__) */#define CONCAT(a, b) a##b#define UNIQUE(base) CONCAT(base, __COUNTER__)int UNIQUE(temp) = 10; /* temp0 */int UNIQUE(temp) = 20; /* temp1 */
If either operand of ## is a macro that should be expanded first, use an intermediate helper macro:
#define PASTE(a, b) a##b#define XPASTE(a, b) PASTE(a, b) /* forces expansion of a and b */#define PREFIX foo_PASTE(PREFIX, bar) /* → PREFIX##bar = PREFIXbar (no expansion) */XPASTE(PREFIX, bar) /* → foo_bar (expansion applied) */
Variadic macros accept a variable number of arguments using an ellipsis (...) as the last parameter. The __VA_ARGS__ identifier in the replacement list is replaced by all the extra arguments, including the commas between them.
#include <stdio.h>/* Logging macros with variadic arguments */#define LOG_INFO(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__)#define LOG_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)LOG_INFO("Server started on port %d", 8080);LOG_ERROR("File not found: %s (errno=%d)", "config.ini", errno);/* Zero extra args */LOG_INFO("Startup complete"); /* Works; ## suppresses trailing comma in MSVC traditional mode */
The C standard requires at least one argument to the ellipsis. The traditional MSVC preprocessor suppresses a trailing comma when no extra arguments are passed (using the ##__VA_ARGS__ extension). The conformant preprocessor (/Zc:preprocessor) does not suppress the trailing comma; use __VA_OPT__(,) (C++20/future C) for strictly conformant code.
/* Always defined */__FILE__ /* Current source filename as a string literal */__LINE__ /* Current line number as an integer constant */__DATE__ /* Compilation date: "Mmm dd yyyy" (e.g., "Jan 5 2025") */__TIME__ /* Compilation time: "hh:mm:ss" (e.g., "14:32:05") *//* Defined within a function body only */__func__ /* Undecorated function name (C99/C11 standard identifier) *//* Example */#define WHERE() printf("%s:%d in %s()\n", __FILE__, __LINE__, __func__)
Language standard version
/* __cplusplus: defined only when compiling as C++ */#ifdef __cplusplus /* C++ mode */#endif/* __STDC_VERSION__: defined when compiling as C with /std:c11 or /std:c17 */#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201710L /* C17 or later */#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L /* C11 or later */#endif/* __STDC__: defined as 1 when /Zc:__STDC__ or /Za is used */
_MSC_VER encodes the major and minor version of the MSVC compiler as a four-digit integer: MMmm where MM is the major version and mm is the minor version.
_MSC_VER value
Visual Studio version
1800
Visual Studio 2013
1900
Visual Studio 2015
1910–1916
Visual Studio 2017
1920–1929
Visual Studio 2019
1930+
Visual Studio 2022
/* Require at least Visual Studio 2019 */#if _MSC_VER < 1920#error This project requires Visual Studio 2019 or later.#endif/* Full version number */printf("Compiler: %d\n", _MSC_VER); /* e.g., 1939 */printf("Full ver: %d\n", _MSC_FULL_VER); /* e.g., 193900000 */printf("Build: %d\n", _MSC_BUILD); /* patch build number */
Platform and architecture macros
/* Windows target (32-bit or 64-bit) */#ifdef _WIN32 /* All Windows builds (includes _WIN64) */#endif#ifdef _WIN64 /* 64-bit Windows only */#endif/* Architecture */#ifdef _M_X64 /* x86-64 (AMD64) target */#endif#ifdef _M_ARM64 /* ARM64 target */#endif#ifdef _M_IX86 /* 32-bit x86 target; value indicates optimization (300, 400, 500, 600, 700) */#endif/* Example: architecture-specific code */#if defined(_M_X64) || defined(_M_ARM64)# define PLATFORM_64BIT 1#else# define PLATFORM_64BIT 0#endif
Build configuration macros
/* Debug build — defined when /MDd, /MTd, or /LDd is used */#ifdef _DEBUG printf("Running debug build\n");#endif/* DLL runtime — defined when /MD or /MDd is used */#ifdef _DLL printf("Using DLL runtime\n");#endif/* __COUNTER__ — unique integer, incremented every use */#define UNIQUE_ID() __COUNTER__int id1 = UNIQUE_ID(); /* 0 */int id2 = UNIQUE_ID(); /* 1 */int id3 = UNIQUE_ID(); /* 2 *//* __TIMESTAMP__ — last modification time of the source file */printf("Source modified: %s\n", __TIMESTAMP__);
You can redefine a macro only if the new definition is identical to the original. Attempting to redefine with a different value produces a warning. Use #undef first if you need a different definition:
#define SCALE 10/* ... */#undef SCALE#define SCALE 20 /* OK — redefinition after #undef *//* Redefine with identical token string — allowed (no warning) */#define SCALE 20#define SCALE 20 /* OK *//* Redefine with different token string — warning C4005 */#define SCALE 20#define SCALE 30 /* warning: 'SCALE' macro redefinition */