125

I often see instances in which using a macro is better than using a function.

Could someone explain me with an example the disadvantage of a macro compared to a function?

Sebastian Nielsen
  • 3,835
  • 5
  • 27
  • 43
Kyrol
  • 3,475
  • 7
  • 34
  • 46
  • 24
    Turn the question on its head. In what situation is a macro better? Use a real function unless you can demonstrate that a macro is better. – David Heffernan Feb 01 '12 at 22:56

11 Answers11

135

Macros are error-prone because they rely on textual substitution and do not perform type-checking. For example, this macro:

#define square(a) a * a

works fine when used with an integer:

square(5) --> 5 * 5 --> 25

but does very strange things when used with expressions:

square(1 + 2) --> 1 + 2 * 1 + 2 --> 1 + 2 + 2 --> 5
square(x++) --> x++ * x++ --> increments x twice

Putting parentheses around arguments helps but doesn't completely eliminate these problems.

When macros contain multiple statements, you can get in trouble with control-flow constructs:

#define swap(x, y) t = x; x = y; y = t;

if (x < y) swap(x, y); -->
if (x < y) t = x; x = y; y = t; --> if (x < y) { t = x; } x = y; y = t;

The usual strategy for fixing this is to put the statements inside a "do { ... } while (0)" loop.

If you have two structures that happen to contain a field with the same name but different semantics, the same macro might work on both, with strange results:

struct shirt 
{
    int numButtons;
};

struct webpage 
{
    int numButtons;
};

#define num_button_holes(shirt)  ((shirt).numButtons * 4)

struct webpage page;
page.numButtons = 2;
num_button_holes(page) -> 8

Finally, macros can be difficult to debug, producing weird syntax errors or runtime errors that you have to expand to understand (e.g. with gcc -E), because debuggers cannot step through macros, as in this example:

#define print(x, y)  printf(x y)  /* accidentally forgot comma */
print("foo %s", "bar") /* prints "foo %sbar" */

Inline functions and constants help to avoid many of these problems with macros, but aren't always applicable. Where macros are deliberately used to specify polymorphic behavior, unintentional polymorphism may be difficult to avoid. C++ has a number of features such as templates to help create complex polymorphic constructs in a typesafe way without the use of macros; see Stroustrup's The C++ Programming Language for details.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Chiara Coetzee
  • 4,201
  • 1
  • 24
  • 20
  • 54
    What's with the C++ advertisement? – Pacerier Sep 27 '13 at 08:58
  • 4
    Agree, this is a C question, no need to add bias. – ideasman42 Sep 10 '14 at 02:55
  • 30
    C++ is an extension of C that adds (among other things) features intended to address this specific limitation of C. I'm no fan of C++, but I think it is on-topic here. – Chiara Coetzee Sep 16 '14 at 22:30
  • 1
    Macros, inline functions, and templates are often used in an attempt to boost performance. They are overused, and tend to hurt performance due to code bloat, which reduces the effectiveness of the CPU instruction cache. We can make fast generic data structures in C without using these techniques. – Sam Watkins Jun 26 '15 at 03:24
  • 1
    According to the ISO/IEC 9899:1999 §6.5.1, "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression." (Similar wording exists in previous and subsequent C standards.) So the expression `x++*x++` cannot be said to increment `x` twice; it actually invokes _undefined behaviour_, meaning that the compiler is free to do anything it wants—it could increment `x` twice, or once, or not at all; it could abort with an error or even [make demons fly out of your nose](https://en.wikipedia.org/wiki/Undefined_behavior). – Psychonaut Sep 01 '15 at 12:09
  • Actually, in most cases you want to prefer the comma operator over `do{}while(0)`. – yyny Aug 30 '16 at 21:22
  • @YoYoYonnY: "Actually, in most cases you want to prefer the comma operator over `do{}while(0)`." --- Why, can you elaborate on that? – Daniel Jul 26 '17 at 15:30
  • Maybe "most cases" was a bit of an overstatement, but my point still holds. The comma operator is specifically designed to execute expressions in sequence. This may circumvent warnings and make the code more optimized. Furthermore, the last expression can be referenced, acting like a return value. `do { ... } while (0)` is more of a hack, like the zero width array struct hack. It works, but you should avoid using it if you don't need it. [GCC's Statement Expressions](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html) combines both of these, so use it if you can. – yyny Jul 28 '17 at 00:11
  • @SamWatkins How? I'm trying to learn something about macros, and whether should I use them in my embedded code. – R1S8K May 06 '19 at 16:39
  • The last example is unlikely to print `foo %sbar`, more likely `foo 2893ey823hd2hed2hbar` or `Segmentation fault`. – Nate Eldredge Apr 08 '21 at 21:11
  • `print("foo %s", "bar")` if that gets substituted to `printf("foo %s" "bar")` is UB. – Deduplicator Aug 02 '22 at 17:46
51

Macro features:

  • Macro is Preprocessed
  • No Type Checking
  • Code Length Increases
  • Use of macro can lead to side effect
  • Speed of Execution is Faster
  • Before Compilation macro name is replaced by macro value
  • Useful where small code appears many time
  • Macro does not Check Compile Errors

Function features:

  • Function is Compiled
  • Type Checking is Done
  • Code Length remains Same
  • No side Effect
  • Speed of Execution is Slower
  • During function call, Transfer of Control takes place
  • Useful where large code appears many time
  • Function Checks Compile Errors
zangw
  • 43,869
  • 19
  • 177
  • 214
  • 12
    "speed of execution is faster" reference required. Any even somewhat competent compiler of the last decade will inline functions just fine if it thinks it'll provide a performance benefit. – Voo May 26 '19 at 20:30
  • 2
    Isn't that, in context of low-level MCU (AVRs, i.e. ATMega32) computing, Macros are better choice, as they do not grow call stack, like function calls do? – hardyVeles Aug 29 '19 at 22:22
  • 3
    @hardyVeles Not so. Compilers, even for an AVR, can inline code very intelligently. Here's an example: https://godbolt.org/z/Ic21iM – Edward Oct 19 '19 at 19:57
38

Side-effects are a big one. Here's a typical case:

#define min(a, b) (a < b ? a : b)

min(x++, y)

gets expanded to:

(x++ < y ? x++ : y)

x gets incremented twice in the same statement. (and undefined behavior)


Writing multi-line macros are also a pain:

#define foo(a,b,c)  \
    a += 10;        \
    b += 10;        \
    c += 10;
        

They require a \ at the end of each line.


Macros can't "return" anything unless you make it a single expression:

int foo(int *a, int *b){
    side_effect0();
    side_effect1();
    return a[0] + b[0];
}

Can't do that in a macro unless you use GCC's statement expressions. (EDIT: You can use a comma operator though... overlooked that... But it might still be less readable.)


Order of Operations: (courtesy of @ouah)

#define min(a,b) (a < b ? a : b)

min(x & 0xFF, 42)

gets expanded to:

(x & 0xFF < 42 ? x & 0xFF : 42)

But & has lower precedence than <. So 0xFF < 42 gets evaluated first.

Anderson Green
  • 30,230
  • 67
  • 195
  • 328
Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 6
    and not putting parentheses with macro arguments in the macro definition can lead to precedence issues: e.g., `min(a & 0xFF, 42)` – ouah Feb 01 '12 at 23:01
  • Ah yes. Didn't see your comment while I was updating the post. I guess I'll mention that too. – Mysticial Feb 01 '12 at 23:07
16

When in doubt, use functions (or inline functions).

However answers here mostly explain the problems with macros, instead of having some simple view that macros are evil because silly accidents are possible.
You can be aware of the pitfalls and learn to avoid them. Then use macros only when there is a good reason to.

There are certain exceptional cases where there are advantages to using macros, these include:

  • Generic functions, as noted below, you can have a macro that can be used on different types of input arguments.
  • Variable number of arguments can map to different functions instead of using C's va_args.
    eg: https://stackoverflow.com/a/24837037/432509.
  • They can optionally include local info, such as debug strings:
    (__FILE__, __LINE__, __func__). check for pre/post conditions, assert on failure, or even static-asserts so the code won't compile on improper use (mostly useful for debug builds).
  • Inspect input args, You can do tests on input args such as checking their type, sizeof, check struct members are present before casting
    (can be useful for polymorphic types).
    Or check an array meets some length condition.
    see: https://stackoverflow.com/a/29926435/432509
  • While its noted that functions do type checking, C will coerce values too (ints/floats for example). In rare cases this may be problematic. Its possible to write macros which are more exacting then a function about their input args. see: https://stackoverflow.com/a/25988779/432509
  • Their use as wrappers to functions, in some cases you may want to avoid repeating yourself, eg... func(FOO, "FOO");, you could define a macro that expands the string for you func_wrapper(FOO);
  • When you want to manipulate variables in the callers local scope, passing pointer to a pointer works just fine normally, but in some cases its less trouble to use a macro still.
    (assignments to multiple variables, for a per-pixel operations, is an example you might prefer a macro over a function... though it still depends a lot on the context, since inline functions may be an option).

Admittedly, some of these rely on compiler extensions which aren't standard C. Meaning you may end up with less portable code, or have to ifdef them in, so they're only taken advantage of when the compiler supports.


Avoiding multiple argument instantiation

Noting this since its one of the most common causes of errors in macros (passing in x++ for example, where a macro may increment multiple times).

its possible to write macros that avoid side-effects with multiple instantiation of arguments.

C11 Generic

If you like to have square macro that works with various types and have C11 support, you could do this...

inline float           _square_fl(float a) { return a * a; }
inline double          _square_dbl(float a) { return a * a; }
inline int             _square_i(int a) { return a * a; }
inline unsigned int    _square_ui(unsigned int a) { return a * a; }
inline short           _square_s(short a) { return a * a; }
inline unsigned short  _square_us(unsigned short a) { return a * a; }
/* ... long, char ... etc */

#define square(a)                        \
    _Generic((a),                        \
        float:          _square_fl(a),   \
        double:         _square_dbl(a),  \
        int:            _square_i(a),    \
        unsigned int:   _square_ui(a),   \
        short:          _square_s(a),    \
        unsigned short: _square_us(a))

Statement expressions

This is a compiler extension supported by GCC, Clang, EKOPath & Intel C++ (but not MSVC);

#define square(a_) __extension__ ({  \
    typeof(a_) a = (a_); \
    (a * a); })

So the disadvantage with macros is you need to know to use these to begin with, and that they aren't supported as widely.

One benefit is, in this case, you can use the same square function for many different types.

Community
  • 1
  • 1
ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • 1
    _"...supported as widely.."_ I bet the statement expression you mentioned isn't supported by cl.exe ? (MS's Compiler) – gideon Jul 24 '15 at 06:06
  • 1
    @gideon, right edited answer, though for each feature mentioned, not sure its necessary to have some compiler-feature-support matrix. – ideasman42 Jul 24 '15 at 06:21
15

Example 1:

#define SQUARE(x) ((x)*(x))

int main() {
  int x = 2;
  int y = SQUARE(x++); // Undefined behavior even though it doesn't look 
                       // like it here
  return 0;
}

whereas:

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

int main() {
  int x = 2;
  int y = square(x++); // fine
  return 0;
}

Example 2:

struct foo {
  int bar;
};

#define GET_BAR(f) ((f)->bar)

int main() {
  struct foo f;
  int a = GET_BAR(&f); // fine
  int b = GET_BAR(&a); // error, but the message won't make much sense unless you
                       // know what the macro does
  return 0;
}

Compared to:

struct foo {
  int bar;
};

int get_bar(struct foo *f) {
  return f->bar;
}

int main() {
  struct foo f;
  int a = get_bar(&f); // fine
  int b = get_bar(&a); // error, but compiler complains about passing int* where 
                       // struct foo* should be given
  return 0;
}
Flexo
  • 87,323
  • 22
  • 191
  • 272
12

No type checking of parameters and code is repeated which can lead to code bloat. The macro syntax can also lead to any number of weird edge cases where semi-colons or order of precedence can get in the way. Here's a link that demonstrates some macro evil

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
6

Adding to this answer..

Macros are substituted directly into the program by the preprocessor (since they basically are preprocessor directives). So they inevitably use more memory space than a respective function. On the other hand, a function requires more time to be called and to return results, and this overhead can be avoided by using macros.

Also macros have some special tools than can help with program portability on different platforms.

Macros don't need to be assigned a data type for their arguments in contrast with functions.

Overall they are a useful tool in programming. And both macroinstructions and functions can be used depending on the circumstances.

KeyC0de
  • 4,728
  • 8
  • 44
  • 68
6

one drawback to macros is that debuggers read source code, which does not have expanded macros, so running a debugger in a macro is not necessarily useful. Needless to say, you cannot set a breakpoint inside a macro like you can with functions.

jim mcnamara
  • 16,005
  • 2
  • 34
  • 51
6

Functions do type checking. This gives you an extra layer of safety.

ncmathsadist
  • 4,684
  • 3
  • 29
  • 45
4

I did not notice, in the answers above, one advantage of functions over macros that I think is very important:

Functions can be passed as arguments, macros cannot.

Concrete example: You want to write an alternate version of the standard 'strpbrk' function that will accept, rather than an explicit list of characters to search for within another string, a (pointer to a) function that will return 0 until a character is found that passes some test (user-defined). One reason you might want to do this is so that you can exploit other standard library functions: instead of providing an explicit string full of punctuation, you could pass ctype.h's 'ispunct' instead, etc. If 'ispunct' was implemented only as a macro, this wouldn't work.

There are lots of other examples. For example, if your comparison is accomplished by macro rather than function, you can't pass it to stdlib.h's 'qsort'.

An analogous situation in Python is 'print' in version 2 vs. version 3 (non-passable statement vs. passable function).

Sean Rostami
  • 141
  • 2
1

If you pass function as an argument to macro it will be evaluated every time. For example, if you call one of the most popular macro:

#define MIN(a,b) ((a)<(b) ? (a) : (b))

like that

int min = MIN(functionThatTakeLongTime(1),functionThatTakeLongTime(2));

functionThatTakeLongTime will be evaluated 5 times which can significantly drop perfomance

Saffer
  • 152
  • 8