Why not macros

From C

Jump to: navigation, search

$Id: why_not_macros.txt,v 1.17 2009/02/13 20:27:52 db Exp db $

The macro preprocessor in C is one of the most abused parts of C, especially for the beginner. In this document I will attempt to outline the case against macros. Note that I am not talking about the common case of a "Manifest constant" i.e.

       #define MAXBUF  1024

this is a fairly harmless use of macros.

I am also not going to talk about the use of #if or #ifdef to turn on or off sections of code.

i.e.

       #if 0     
       code to ignore
       #endif

is an often used technique for disabling fragments of code that you wish to debug. However, I shall return to the mis-use of #if's and #ifdef's in a later document.


1. Macros have side effects

Users have to remember that macros are a simple text expansion into the source code before the c compiler itself compiles the code.

Here is one example of side effects from macros.

       #define MAX(a,b)        ((a>b)?a:b)
       int     i,j=1,k=2;
       i = MAX(j++,k++);

What is k now? Since macros expand out as text you end up with:

       i = (j++>k++?j++:k++)

j is 1, so is less than j which is 2, j gets incremented then k gets incremented so j is now 2 k is now 3. And since k is returned from the tertiary operator as k is larger, k gets incremented again, so you now have 4, not 3, i has the expected value of 3.

2. Macros can obsfuscate code

Not only can macros be very hard to read in of themselves, but users get carried away with them, throwing in a macro "to make it simpler" for every little thing that has to be done "fast". This can lead to cascades of macros calling macros calling macros that are defined all over the place, making it very hard to see what a macro is actually doing.

3. Macros tend to be hard to read and can be buggy

As there is no proper return possible from a macro because a macro's result is the execution of its literal replacement, this often means multiple tertiary-operators in multiple continuation lines. These can be unbelievably hard to read and can hide bugs for many years. Just imagine what a misplaced bracket can do.

4. Macros can make your code harder to debug

As stated a macro is expanded into your code each time it is seen, this means if there is a bug that causes a SIGSEGV (core) it can be difficult placing a breakpoint or seeing exactly where the bug is especially in a longer macro. This is not a problem with a function.

5. Macros can actually slow your code down instead of speeding it up

One thing a lot of programmers forget is cache effects, a modern CPU as typically used by the beginner C programmer, will pre-load as much code to execute into a cache as it can before it begins to actually execute it. In some cases, code prefetched into the cache is pre-cracked to make it easier for the actual processor to work upon. I have glossed over the details, because what exactly happens here is beyond the scope of this document. The point is, there is only a certain amount of memory available for a cache on an instruction stream. If you have filled up your cache with repeated expansions of the exact same code, you can actually make your code slow down, not speed up; Not what you had in mind. Your code can actually run faster by using function calls over macros or inline functions. Note that some compilers will inline code to "speed it up" as well as unroll loops; gnu gcc is bad for this with the -O3 option. This option can lead to buggy code and slower code.

If you have read this far and are still sure you need a macro.

1. At least consider using an inline instead.

As was mentioned in the first section, macros are expanded pre-compile time as pure text which the compiler then compiles. This means you are inserting the same code over and over again, i.e. a primitive form of inlining code without the readibility of C. Macros were seen as a method of making code "faster" somehow through the magic of inlining. This may have been true of very slow machines of yesteryear, but nowadays modern machines it is very inexpensive to make a function call. This is a symptom of "premature optimisation". (This will be the subject of another "gotcha".) It is the opinion of this writer that modern machines and compilers do not need any user hints on how to best to compile/inline code to achieve best space/speed tradeoffs. But if you are going to do a macro for speed, at least use an inline. As I have stated I do not personally believe inlines make any sense in this day and age, but at least they are somewhat more readable.

--Dianora 20:36, 13 February 2009 (UTC)

Personal tools