Skip to content

22 Beginner The Hundred

The Macro That Did the Wrong Math

a 4×4 square with a suspicious area

#include <iostream>

#define SQUARE(x) (x * x)

int ids = 0;

int next_id() { return ++ids; }

int main() {
    int side = 3;
    int border = 1;

    std::cout << "framed area:    " << SQUARE(side + border) << '\n';
    std::cout << "id squared:     " << SQUARE(next_id()) << '\n';
    std::cout << "ids handed out: " << ids << '\n';
}

Run it. How big is a 4×4 square, and how many ids get handed out?

Answer

7, 2, 2 — not 16, 1, 1. The square lost nine units of area, and one id was quietly spent twice.

Why

A macro is not a function: the preprocessor pastes the argument's text into the body and the compiler parses whatever comes out. So SQUARE(side + border) becomes (side + border * side + border) — the outer parentheses were never the problem, because * binds tighter than +, and the result is 3 + 1·3 + 1 = 7. And SQUARE(next_id()) becomes (next_id() * next_id()): the argument text appears twice in the body, so the call happens twice, yielding 1 and 2 (in unspecified order) for a product of 2. -Wall -Wextra is silent on both — by the time the compiler sees this, it is ordinary, well-formed arithmetic. Parenthesizing each parameter (#define SQUARE(x) ((x) * (x))) fixes the precedence, prints 16, and still calls next_id() twice; with SQUARE(i++) that double evaluation is undefined behavior — modifying i twice with nothing sequencing the two (GCC 11.5 does warn there: operation on 'i' may be undefined [-Wsequence-point], on under plain -Wall). When a macro misbehaves, g++ -E main.cpp shows you the text it really produced.

The fix

Use a function. It respects precedence, evaluates its argument exactly once, and constexpr keeps it foldable at compile time:

constexpr int square(int v) { return v * v; }

square(side + border);   // 16 — the argument arrives as a value, not as text
square(next_id());       // one call, one id

Takeaway: macros paste text, functions take values — if it computes something, make it a constexpr function.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Quiz this entry