Explore function-like macros that can accept arguments, and understand their benefits and pitfalls.
Beyond simple constant substitution, the `#define` directive can be used to create powerful **function-like macros**. These macros can accept arguments, just like real functions, making them a powerful tool for creating short, inline code snippets. A function-like macro is defined with its name followed immediately by a list of parameters in parentheses. For example, `#define SQUARE(x) (x) * (x)`. When the preprocessor encounters `SQUARE(5)` in the code, it will replace it with `(5) * (5)`. The main benefit of using a macro over a function is speed. Since the macro's code is directly substituted into the source code, it avoids the overhead of a function call. This can be significant for very simple operations that are called many times inside a loop. However, macros come with significant pitfalls that require careful handling. The text substitution is literal and not type-safe. The compiler does not check the type of the arguments passed to a macro. A more subtle issue arises from operator precedence. In our `SQUARE` example, if we called `SQUARE(2 + 3)`, it would expand to `(2 + 3) * (2 + 3)`, which is correct. But if we had defined it as `#define SQUARE(x) x * x`, the expansion would be `2 + 3 * 2 + 3`, which evaluates to 11, not 25. This is why it is a critical best practice to enclose every argument and the entire macro body in parentheses to avoid unexpected behavior. While powerful, for most cases, modern compilers can inline regular functions effectively, making them a safer alternative to complex macros.