30

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 minimized or where an argument must not be evaluated more than once.

What does he mean by "an argument must not be evaluated more than once?"

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Noman Hanafi
  • 377
  • 2
  • 8
  • 8
    Schildt is [seen sceptical by some](https://en.wikipedia.org/wiki/Herbert_Schildt#Reception). Just sayin'. – DevSolar Mar 14 '16 at 16:41
  • 3
    @DevSolar: I don't know that book, but if the cited statement is typical for the style, I fully agree with the sceptics. – too honest for this site Mar 14 '16 at 18:26
  • @DevSolar You meant [skeptical](https://www.google.com/search?q=define%3Askeptical), right? Or did you spell it with a [C](https://en.wikipedia.org/wiki/C_(programming_language)) because this question is tagged [tag:c] and not [tag:k]? :P – cat Mar 14 '16 at 21:47
  • 5
    @tac [It's only "skeptical" in the USA's variant of the language](https://en.wikipedia.org/wiki/American_and_British_English_spelling_differences#Hard_and_soft_.22c.22) – Lightness Races in Orbit Mar 15 '16 at 01:52
  • 3
    Regardless of which side of the pond you inhabit, it's being used as an adverb in that sentence, so it should be sceptically/skeptically. – Cody Gray - on strike Mar 15 '16 at 07:00
  • Related: http://stackoverflow.com/questions/17043090/why-should-i-avoid-macros-in-c and http://stackoverflow.com/questions/16243509/explanation-of-c-faqs-unsafe-macro (not actually duplicates, since this question is tagged C, but the problems are nearly identical in both languages) – Cody Gray - on strike Mar 15 '16 at 07:03

2 Answers2

50

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.

Justin
  • 24,288
  • 12
  • 92
  • 142
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I think what you said is opposite. For the function call `max(x, x++)`, `x` is evaluated twice so in this case using macro will be fine. In macro call `MAX(x++, y++);`, none of the argument evaluated more than once. – haccks Mar 14 '16 at 17:26
  • 5
    @haccks That is incorrect. As Joachim says in his answer `MAX(x++, y++)` is expanded to `((x++) < (y++) ? (x++) : (y++));` and is therefore evaluated twice. The corresponding function `max(x++, y++)`, each argument is only evaluated once – kmdreko Mar 14 '16 at 17:46
  • 3
    @haccks Both the `x++` and the `y++` expressions are evaluated as part of the condition. Then one of either `x++` or `y++` is evaluated once again depending on the condition in the ternary expression. – Some programmer dude Mar 14 '16 at 18:14
  • @JoachimPileborg; I am talking about "argument", not about expressions of ternary operator. – haccks Mar 14 '16 at 18:24
  • 3
    @haccks But a macro is simply *expanded* to the body of the macro, there's no evaluation of the "arguments" since those a simply pasted into the expanded macro. When using a macro such as `MAX` as defined in my answer, then it's the preprocessor which handles the macro expansion, the compiler proper doesn't see the macro invocation it only sees the expanded ternary expression. – Some programmer dude Mar 14 '16 at 18:26
  • So, you are saying that the `x++` and `y++` in the expanded expression are arguments? – haccks Mar 14 '16 at 18:32
  • @haccks: No, in the original source code they are macro arguments. After expansion they are operands, not arguments, but the coder writing `MAX(x++, y++)` shouldn't have to know what the expansion is in order to use the macro correctly. – Ben Voigt Mar 14 '16 at 18:42
  • 1
    Second problem is mitigated by `#define MUL(a, b) ((a) * (b))` – Erbureth Mar 14 '16 at 20:26
  • 1
    I don't think your second example is helpful for this question. As Erbureth said, you can fix that by adding some parentheses--so it's an example of using a poorly formed macro rather than being an example of using a macro where you should be using a function. – Carmeister Mar 15 '16 at 01:17
  • @haccks `max(x,x++)` is undefined behaviour (whether or not it is a macro) due to writing and separately reading variable without a sequence point in between – M.M Mar 15 '16 at 04:03
  • 1
    @Carmeister I added it because it's easy to forget things like that when writing macros, and then it blows up in your face. I also added it as another push to use functions, since [inline functions](http://en.cppreference.com/w/c/language/inline) was introduced in C99, there's really no need to use function-like macros anymore except in legacy code. – Some programmer dude Mar 15 '16 at 06:27
4

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.

nalzok
  • 14,965
  • 21
  • 72
  • 139