5.1.28 · D1C Programming

Foundations — Macros vs inline functions

1,819 words8 min readBack to topic

Before you can judge that battle, you must own every symbol the parent note throws at you. Below, each piece is built from nothing: plain words → a picture → why the topic needs it. Read top to bottom; each rung stands on the one below.


1. What a "line of source code" actually is

Figure — Macros vs inline functions

Look at the figure: the same line SQUARE(2 + 3) is shown twice. On the left a hand simply copies the letters (that is the preprocessor's view). On the right the letters are first understood as numbers and operations (that is the compiler's view). Keep this picture in your head for the entire topic — it explains everything that follows.


2. The two translators: preprocessor and compiler

#define SQUARE(x) ((x) * (x))   // <- the # means: preprocessor, handle me

The # is not decoration. It is a flag that says: "this line is for the first translator, not the second." Everything after #define is a rule the preprocessor obeys literally.


3. The symbol #define — a find-and-replace rule

Figure — Macros vs inline functions

Follow the arrows in the figure. x is a hole. Whatever text you drop into the hole gets stamped into every place x appears in the body — as characters, uncalculated. This is why the parent's SQUARE(2 + 3) becomes the literal string ((2 + 3) * (2 + 3)): the four characters 2 + 3 are pasted twice, verbatim.


4. Parentheses ( ) and operator precedence

Figure — Macros vs inline functions

The figure shows both trees for #define BAD(x) x*x used as BAD(2+3). Without parens the pasted text is 2+3*2+3; precedence makes * grab only the adjacent 3 and 2, giving the red wrong subtree = 11. With parens, the walls force (2+3) to resolve as one unit before squaring, giving 25. The trap is not in the macro — it is in precedence acting on carelessly pasted text.


5. The symbols x++ and "side effect"

#define SQ(x) ((x)*(x))
int i = 3;
int r = SQ(i++);   // becomes ((i++)*(i++)) -> i changed twice

An inline function sidesteps this because the argument i++ is computed once into a parameter before the body ever runs — a concept we build in section 7.


6. The symbol inline and "function call overhead"

Figure — Macros vs inline functions

The figure contrasts two paths. The left path calls a function: control leaves the current spot, travels to the function, and returns — the round-trip is the overhead. The right path has the function's body pasted in place (inlined), so there is no trip at all. Both macros and inline functions aim for the right-hand picture; they just choose different translators to do the pasting.

static inline int square(int x) { return x * x; }

7. What "type checking" and "scope" give you


Prerequisite map

Source code is plain text

Preprocessor edits text

Compiler understands C

define replaces text

Function-like macro

Operator precedence

Parentheses force grouping

Side effect of plus plus

Double evaluation

Type checking and scope

Inline function

Function call overhead

Macros vs inline functions


Equipment checklist

Test yourself — cover the right side.

Is a .c file text or already machine code before translation?
Just plain text — meaningless to the CPU until translated.
Which translator runs first, preprocessor or compiler?
The preprocessor, doing pure text edits before the compiler.
What does #define SQ(x) ((x)*(x)) literally do to SQ(5)?
Pastes the body with x replaced by 5, giving ((5)*(5)) as text.
Does the preprocessor calculate 2+3?
No — it only copies characters; the compiler does the arithmetic later.
Why does * grab pieces of 2+3*2+3?
Because * has higher precedence than +, so it binds before addition.
What do parentheses do to precedence?
They force grouping, making the inside resolve first regardless of operators.
How many times does i++ run in ((i++)*(i++))?
Twicei is incremented twice, the double-evaluation trap.
What is function call overhead?
The cost of jumping, saving registers, and returning — the round-trip both techniques remove.
Is inline a guarantee?
No — a request/hint the compiler may ignore.
Name one gift of being a real function (not a macro).
Type checking, single argument evaluation, scope, debuggability, or having an address.

Connections

  • Parent topic ↑
  • C Preprocessor — the first translator that does the text pasting
  • Operator Precedence in C — why parentheses in macros are non-negotiable
  • Function Call Overhead — the cost these techniques exist to remove
  • Undefined Behavior in C — where double evaluation leads
  • const and constexpr — typed alternatives to constant macros
  • Compiler Optimization — when inline is honored