Herbert Schildt says:
In some situations, real function should be used in place of function-like-macro, for example: where code size is to be minimize
Let's take a macro to calculate the maximum of two values:
#define MAX(a, b) ((a) < (b) ? (a) : (b))
Then we use it like this:
int x = 5;
int y = 10;
int max = MAX(x++, y++);
Then the macro is expanded to
int max = ((x++) < (y++) ? (x++) : (y++));
As you can see, the increment operation on either x
or y
will happen twice, not what would happen if you had a function where each argument you pass is evaluated only once.
Another important point is the use of parentheses in the macro. Let's take another simple macro:
#define MUL(a, b) a * b
Now if you invoke the macro as
int sum = MUL(x + 3, y - 2);
then the expansion becomes
int sum = x + 3 * y - 2;
Which due to operator precedence is equal to
int sum = x + (3 * y) - 2;
Often not quite what was expected, if one expects (x + 3) * (y - 2)
.
This problem is also "solved" by using functions.
Sometimes arguments have side effects.
For example, the value of i++
is i
, but i
is increased by 1. As a result, the value of next i++
will be i + 1
.
In a macro, arguments are evaluated every time it's called for, causing resulting values; In a function, the (actual) arguments are evaluated and copied to (formal) arguments inside the function, dismissing the side effects.
When inplementing a function, you don't care about side effects. However, implicit type promotion and casting may be error-prone instead.