Code Craft: When #define Is Considered Harmful

An icon of Computer Science, [Edsger Dijkstra], published a letter in the Communications of the Association of Computer Machinery (ACM) which the editor gave the title “Go To Statement Considered Harmful“. A rousing debate ensued. A similar criticism of macros, i.e. #define, in C/C++ may not rise to that level but they have their own problems.

Macros are part of the preprocessor for the C/C++ languages which manipulates the source code before the actual translation to machine code. But there are risks when macros generate source code. [Bjarne Stroustrup] in creating C++ worked to reduce the need and usage of the preprocessor, especially the use of macros. In his book, The C++ Programming Language he writes,

Don’t use them if you don’t have to. Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer.

As C retrofitted capabilities of C++, it also reduced the need for macros, thus improving that language.

With the Arduino using the GNU GCC compilers for C and C++ I want to show new coders a couple of places where the preprocessor can cause trouble for the unwary. I’ll demonstrate how to use language features to achieve the same results more cleanly and safely. Of course, all of this applies equally when you use any of these languages on other systems.

We’re only going to be looking at macros in this article but if you want to read more the details about them or the preprocessor see the GNU GCC Manual section on the preprocessor.

Basic Macro Usage

The preprocessor is complex, but described in simplified terms, it reads each line in a compilation unit, i.e. file, scanning for lines where the first non-whitespace character is a hash character (#). There may be whitespace before and after the #. The next token, i.e. a set of characters bounded by whitespace, is the name of the macro. Everything following the name is the argument. A macro has the form:

#define <name> <rest of line>

The simplest macro usage is to create symbols that are used to control the preprocessor or as text substitution in lines of code. A symbol can be created with or without a value. For example:

#define LINUX 
#define VERSION 23 

The first line defines the symbol LINUX but does not give it a value. The second line defines VERSION with the value 23. This is how constant values were defined pre-C++ and before the enhancements to C.

By convention, macro symbol names use all caps to distinguish them from variable and function names.

Symbols without values can only be used to control the preprocessor. With no value they would simply be a blank in a line of code. They are used in the various forms of the #if preprocessor directives to determine when lines of code are included or excluded.

When a symbol with a value appears in a line of code, the value is substituted in its place. Here is how using a macro with a value looks:

const int version_no = VERSION; 

which results in the code

const int version_no = 23; 

This type of macro usage doesn’t pose much of a threat that problems will arise. That said, there is little need to use macros to define constants. The language now provides the ability to declare named constants. One reason macros were used previously was to avoid allocating storage for a value that never changes. C++ changed this and constant declarations do not allocate storage. I’ve tested this on the Arduino IDE, and found that C does not appear to allocate storage but I’ve seen mention that C may do this on other systems.

Here is the current way to define constants:

const int version = 23;
enum {start=10, end=12, finish=24};   // an alternative for related integer consts

Function Macros

Another form of macro is the function macro which, when invoked looks like a function call, but it is not. Similar to the symbol macros, function macros were used to avoid the overhead of function calls for simple sequences of code. Another usage was to provide genericity, i.e. code that would work for all data types.

Function macros are used to pass parameters into the text replacement process. This is fraught with danger unless you pay close attention to the details. The use of inline functions is much safer as I’ll show below.

To illustrate here’s an example of a function macro to multiply two values.

#define MULT(lhs, rhs) lhs * rhs

This function macro is used in source code as:

int v_int = MULT(23, 25);
float v_float = MULT(23.2, 23.3);

Consider this use of the macro, its expansion, and its evaluation, which definitely does not produce the expected result:

int s = MULT(a+b, c+d);
// translates to: int s = a + b * c + d;
// evaluates as: a + (b * c) + d

This can be addressed by adding parenthesis to force the proper evaluation order of the resulting code. Adding the parenthesis results in this code:

#define MULT(lhs, rhs) ((lhs) * (rhs))
int s = MULT(a+b, c+d);
// now evaluates as: (a + b) * (c + d)

The parenthesis around lhs force (a + b) to be evaluated before the multiplication is performed.

Another ugly case is:

#define POWER(value) ((value) * (value))
int s = POWER(a++);
// evaluates as: ((a++) * (a++))

Now there are two problems. First, a is incremented twice, and, second, the wrongly incremented version is used for the calculation. Here again it does not produce the desired result.

It’s really easy to make a mistake like this with function macro definitions. You’re better off using an inline function which is not prone to these errors. The inline equivalents are:

inline int mult(const int x, const int y) { return (x * y); }
inline int power(const int x) { return (x * x); }

Now the values of x and y are evaluated before the function is called. The increment or arithmetic operators are no longer evaluated inside the actual function. Remember, an inline function does not produce a function call since it is inserted directly into the surrounding code.

In C, there is a loss of generality using inline over the macro. The inline functions shown only support integers. You can add similar functions for different data types, which the standard libraries do, but the names must reflect the data type. A few cases would be covered by mult_i, mult_f,  mult_l, and mult_d for integer, float, long and double, respectively.

This is less of a problem in C++ where there are two solutions. One is to implement separate functions, as in C, but the function names can all be mult relying on C++’s ability to overload function names.

A nicer C++ version is to use template functions. These really are straightforward for simple situations. Consider:

template &lt;typename T&gt;
inline T mult(const T x, const T y) { return (x * y); }
template &lt;typename T&gt;
inline T power(const T x) { return (x * x); }

You use these just like any other function call and the compiler figures out what to do. There is still one minor drawback. The mult cannot mix data types which MULT has no problem doing. You must use an explicit cast to make the types agree.

The code generated by the inline and template versions are going to be the same as the macro version, except they will be correct. You should restrict the use of macros to preprocessing of code,  not code generation. It’s safer and once you are used to the techniques it’s easy.

If these problems aren’t enough, take a look at the GNU preprocessor manual section which provides more details and examples of problems.

Stringification and Concatenation

The previous sections discussed the problems with macros and how to avoid them using C/C++ language constructs. There are a couple of valuable uses of macros that we’ll discuss in this section.

The first is stringification which converts a function macro argument into a C/C++ string. The second is concatenation which combines two arguments into a single string.

A string is created when a # appears before a token. The result is a string: #quit becomes “quit”.

Two arguments are concatenated when ## appears between them: quit ## _command becomes quit_command.

This is useful in building tables of data to use in a program. An illustration:

#define COMMAND(NAME) { #NAME, NAME ## _command }

struct command commands[] =
COMMAND (quit),
COMMAND (help),

expands to the code

struct command
char *name;
void (*function) (void);

struct command commands[] =
{ &quot;quit&quot;, quit_command },
{ &quot;help&quot;, help_command },


The C/C++ preprocessor is powerful and dangerous. The standards committees have followed Stroustrup’s lead in adding features that reduce the need to use the preprocessor. There is still a need for it and probably always will be since it is an inherent part of the languages. Be careful when and how you use #define, and use it sparingly.

95 thoughts on “Code Craft: When #define Is Considered Harmful

  1. I’m going to go ahead and duck for the incoming religious debate to follow…

    I’m not sure what stirs emotion more. God vs. Allah or VI vs EMACS or Linux vs BSD or C vs C++ or Coding style A vs Coding style B… etc etc. But I think the HaD televangelist above, beloved Brother Rud, just picked a fight.

  2. I’ve always liked these “don’t use *this* feature, because other people have written bad code” arguments.

    Bad code is an aspect of the programmer, not the features you use. Lots of people eschew Perl for that reason, yet well structured Perl is perfectly readable and has lots of advantages over other languages.

    Join the crusades if you like. If a client has a coding standard then I’ll use it, but otherwise I use all available tools.

    Don’t blame the feature because someone else was clueless.

    1. I am not a very good programmer although I’ve got some skills for debugging and tweaking code. I tend to avoid programmes written in C++ because it always takes me four times as much time to find a way to tweak them than I need to do a comparable change in C.

    2. I somewhat agree, unfortunately. It is unfortunate because it hinders readability of the code and debugging a macro issue can be frustrating. I hate magic macros that get around language restrictions/hide complexity. I also truly hate macros used for constants; it’s a scoped language use scope and scope appropriate constants.

      However there comes a time when either for convenience or necessity you have to do the devilish things that get code conformity nazis all up in arms.

      The big trend now is don’t use raw pointers, however there exist times when you have monkeyed around with the code enough or you want to keep a clean interface in which raw pointers are the only things that make sense. I think functions that take or return smart pointers look terrible. Reminds me of standard library function declarations.

      1. We’re not allowed to use raw pointers anymore?

        I use raw pointers all the time, not my damn fault if the programmer after me is so fresh they have to be hand held debugging something as fundamental as pointers.

        1. The cost of software isn’t simply writing it. It’s about owning it over the long term. You may write code that works perfectly, but if it’s hard for someone else to read, or hard for them to reuse, it’s generally not a bargain to own.

    3. That’s an overly simplistic view, in my mind, because language features can vary as to how much they encourage or facilitate mistakes. For instance, “go to considered harmful” was because it makes it very easy to write spaghetti code – and it’s true that broadly speaking, code with fewer gotos is almost always easier to read than code with more.

      Likewise, for instance, C’s use of null-terminated strings invites mistakes, as do functions like gets. Yes, it’s possible to use them properly (except perhaps gets), but they’re designed in a way that makes it very easy to use them badly.

  3. #define COMMAND(NAME) { #NAME, NAME##_command }

    There should not be any spaces around the ## operator (that’s kinda the point of that operator: it lets you tell the preprocessor that part of a string needs to be handled separately (and usually replaced by something else) from another string.

    By the way, speaking of Arduino… My favorite Arduino macro is this:

    #define KEEP_YOUR_MITTS_OFF(x) x

    typedef struct MyStructType


    void ThisFunctionDoesntCompile(MyStructType m)


    KEEP_YOUR_MITTS_OFF(void ThisFunctionCompilesFine(MyStructType m))


    The Arduino IDE assumes that it’s smarter than you and tries to find all functions, and generates a prototype for each of them including the ones that use types that you declare. Those prototypes get inserted at the top of your program and of course the custom types aren’t defined there yet so compilation fails with a vague error message. The macro keeps the Arduino preprocessor from generating a prototype so functions that use typedef’d types in parameters or return value can be compiled without problems if used in the normal C/C++ way.

    1. Cute macro. I haven’t seen the problem it fixes but I’ll keep it in mind.

      This code works fine in the IDE. You can use spaces around the ## operator.

      #define test(a, b) a ## b

      test(const in, t abcd = 0);

    2. Whenever I write an interface to something, I make a .h file with the specifics and encapsulating the useful functionality (such as Init(), SetFrequency(), and so forth).

      At the top of each is a set of macros, like the ones below, that specify which port and bit is associated with the device. The implementation then uses token pasting to initialize the correct interface.

      Each new project gets a local copy of the .h file with the settings specific to the circuit. Saves a lot of development time.

      // Specify the RESET output port
      // Specify the FQ_UD output port
      #define AD9850_RESET_PORT D
      #define AD9850_RESET_PIN 4

      #define AD9850_FQ_UD_PORT D
      #define AD9850_FQ_UD_PIN 5

      1. It reminds me of a Moto 68332 project that was just starting up. This was mid-90s. I was handled this large manual on configuring the registers for the ‘332. There were registers to control everything. After digesting it I proceed to create #defines for every register, bit, and field and macros for combining them. It went on for pages.

        But a hardware engineer looked at the end result and said, “With this, even I can setup the registers for startup on the ‘332”.

        Not sure how I would tackle it today if they have a C++ compiler for it.

        1. most probably using enums. By doing that you can then define functions for configuring your device that use the enum types and thanks to the type system prevent wrong use of your device api.

      2. Instead of the RESET_PORT and RESET_PIN, I would define ad9850_reset_on( ) and ad9850_reset_off( ) macros or inline functions. The focus should be on the result, not the means.

      3. If you want to save a bit of writing, you can adjust your macros to do something along these lines:

        #define AD9850_RESET D,4
        #define AD9850_FQ_UD D,5

        #define _PORT(GROUP, BIT) PORT ## GROUP
        #define _BIT(GROUP, BIT) BIT

        #define SET_PIN(DESC) _PORT(DESC) |= (1 << _BIT(DESC))
        #define CLEAR_PIN(DESC) _PORT(DESC) &= ~(1 << _BIT(DESC))


        Personally, I have autogenerated definitions for each pin of the MCU, which have the data in the format "..,D,4" and the relevant macros have an additional unused CANARY argument. This is just so that if someone ever accidentally uses one of the pin data macros outside of a pin handling macro, the ".." should always generate a compiler error, no matter where it ends up. (So to be clear, I'd say something like "#define AD9850_RESET _PD4")

        If you need to pass the pin data deeper into nested macros without it breaking up into individual arguments, you can do it with a PASS macro:

        #define PASS(X) X
        #define TOGGLE_PIN(DESC) _PORT(DESC) ^= (1 << _BIT(DESC))
        #define STROBE_PIN(DESC) do {TOGGLE_PIN(PASS(DESC)); TOGGLE_PIN(PASS(DESC));} while(0)

        PS. Yes, I know you shouldn't define macros with a leading underscore. Feel free to name them eg. EXTRACT_PORT etc.
        PPS. I also have some truly unholy macros for other related pin management stuff. It'd take way too much space to explain here, though. In case someone likes macro abuse, do not fret; I will definitely write it up at some point!

          1. Well, I didn’t really come to debate whether you should use macros or other methods, I just came to give a person who uses macros one way an alternative way that needs less repetition.

            BUT… Yeah, you could use static inline functions. Define an ad9850_reset_on() and ad9850_reset_off(), like someone already commented, and whatever else you happen to need for the pin in question. Then when you need to change the pin for whatever reason, you’ve got N functions where you need to change the port and the pin. And of course you have to change it where you configured the pin mode, too. Hope you didn’t forget any place. Meanwhile, the guy who used macros just changed one line and was done.

            And I could go on and on about the more complicated (or “evil”, if you prefer) things you could do, but my point is simply this: You can say macros are “ugly” and “evil” and whatnot, but there are some things that macros just make a lot easier, things that you really can’t do reasonably without macros, whether you like it or not.

            But really, I’m not here to say you need to use macros, if you don’t see the need. Just like I don’t need to stop using macros where I’ve found they bring benefits. I just wanted to share a shorter way of writing those macros, for the people who do see the need.

            That said, if you *do* know some trick for doing this nicely with static inline functions, so that you can change the pin definition on only a single line, please share, I’m definitely interested. Currently, I have macros set up so that if I change hardware so that pins move around, I only need to change one line per pin. If I port from one architecture to another, in addition to the previous, I need to swap the pin macro file to one for the other architecture so that the macros refer to the correct registers. Of course, that only takes care of porting all the GPIO, you still have to port all peripheral use etc. normally. But this makes porting the GPIO part much easier and less error-prone, as you only need to change the port and pin on one line.

          2. Programming is not about having the least line of code or having finished writing the code the quickest. I don’t see the problem in writing a few functions that do what they suggest instead of manipulating some “virtual” object of which is not immediately clear what it does and how it works. The generated code is the same…

    3. I used to call this macro TRICK17 ( However by now I found a better solution. Put the structures and functions into the anonymous namespace. Since the sketch files are not libraries it does not matter for the compiler. However the Arduino preprocessor seems to be unaware of namespaces. Thus it will not try to preprocess anything in namespace.

  4. “You use these just like any other function call and the compiler figures out what to do. There is still one minor drawback. The mult cannot mix data types which MULT has no problem doing. You must use an explicit cast to make the types agree.”

    The most portable (least feature requiring way) to do this “right” is:
    inline void mult(T1& result, const T2& x, const T3& y) { result = (x * y); }
    … This will work exactly as expected on basically all C++ compilers. Start here.

    If passing output variables offends you too much, very new compilers (c++11) let you do decltype with auto:
    inline auto mult(const T1& x, const T2& y) -> decltype(x*y)
    return (x * y);

    Very, very new compilers (c++14) let you move the decltype to the beginning
    inline decltype(auto) mult2(const T1& x, const T2& y)
    return (x * y);
    (compiles and works with g++ -std=c++14)

    1. ARGH! markup escaping ate my <'s
      replace |'s with
      inline void mult(const T1& x, const T2& y, T3& result) { result = (x * y); }

      template |typename T1, typename T2>
      inline auto mult(const T1& x, const T2& y) -> decltype(x*y)
      { return (x * y); }

      template |typename T1, typename T2>
      inline decltype(auto) mult2(const T1& x, const T2& y)
      return (x * y);

  5. Please use them when you should. Almost every macro demonstrates great features in the preprocessor, additional function in the program, and a display of how lazy human beings can be :)

    1. The difference is that there is still some level of type protection, and at least in most cases they are readable to some degree.

      I will concede that over use is a real issue. This is especially true for the use shown when accepting all numeric types. There really should be a generic numeric type for just this case as most/all arithmetic operations would be successful and the result expected. In fact I have done just this in a few projects. It really cleans up code.

  6. When you need to use sentences like “I’ve seen mention that” (without even a link to where, but that’s another issue), you should probably refrain from trying to teach a subject you don’t really master.

  7. Definitely agree – #defines, especially in C, expose quite a few language deficiencies. I’ve generally tried to stick with consts, but there are a few cases where the compiler still becomes unhappy. The most common is if you’re trying to keep manageable constants for, say, the size of some buffer. You can’t size it with a const, because (please correct me, might not be entirely true) a const isn’t necessarily guaranteed to not /change/, but just means you can’t /change/ the variable. (Think – you might define a read-only register for some peripheral as volatile const). Basically, it’d be nice to have typed compile-time types for that.

    Worse yet, some compilers for lower end chips have far dumber compilers. There is a significant performance difference between static inline void function(int a, int b) and #define function(a,b) (I use this extensively to keep my code organized and abstract out hardware-dependent features), and some compilers are still too dumb to not allocate memory to unused consts. Unfortunately, low-end 8-bit micros are exactly where this matters. Sure, there are more powerful chips, but sometimes you need the lower power consumption or lower per-chip cost, but still want to keep your code maintainable (i.e. not write assembler). A long time ago, before Arduino and before AVR was a platform you could easily find documentation on, I started off into uC-land with writing “PIC14” (the lower end of their 8-bit cores, i.e. PIC10/12/16 and not PIC18) assembler. Before long, I transitioned to ARM and started using C extensively, and thought everyone insisting on assembly on PIC was delusional.

    In the last few weeks, I’ve been working on a big project to port a few things back to the PIC platform, and I totally understand – most of the compilers in that market are a total disaster, and you could easily spot places where the compiler is emitting unnecessary instructions. Worse yet, half the compilers available barely follow any version of ANSI C (one choked when I tried to specify an argument as a const – it’s not a valid keyword for parameters, apparently!) If you’re an experienced C coder and want to see something horrific, give MikroE or CCS C a spin. XC8/Hi-Tech C and the beta SDCC port are much more acceptable, but I saw plenty of trash instructions or unoptimized code emitted in my tests. XC8 of course has to be run in pro mode (free to evaluate for 30 days, luckily) for any reasonable testing – you’ll wonder if they deliberately designed their compiler to emit trash in Free mode, if you look at the assembler output.

    Anywho, my solution? I gave up on trying to use typed constructs and just went ahead and used #defines in my compiler abstractions. There really still isn’t much of a choice in many embedded use cases.

    1. I think XC8 might generate badly unoptimized code on purpose to make their “powerful code optimization at better than 50% when compared to the free edition” claim true. And why they made peripheral libs only for PIC18s?
      For some time I used MikroPascal because I hated C. I still hate C, but I can at least use it now without resorting to violence whenever compiler starts crying in the corner because it misses a semicolon.

      I never write any #defines. I just use those generated by IDE for fusebits or those written by Microchip in their header files, thus avoiding major headaches. If I need constants, I use constants, they were included in many programming languages for this purpose, so why anyone would use #define instead? And it’s easy to modify constants (for example calibration tables) from firmware, which is useful with EEPROMless PICs.

    2. The reason why PIC compilers, generally are so bad is because the architecture of a PIC is so badly designed for supporting high-level languages, easily the worst processor on the market for that job. The 8-bit PIC’s lack of support for addressable stacks in main memory; kludgy treatment of pointers (e.g. PCLATH or BSR); lousy single-addressed instructions; banked RAM (a true horror); a lack of proper indexed addressing and few registers (it’s better to consider the PIC as an accumulator architecture, since the ‘file registers’ are really just the RAM space).

      This has the consequence that (a) 8-bit PIC programmers focus on the peripherals rather than the core, so they end up thinking that the architecture doesn’t matter, which leads to more badly coded projects. (b) It’s much harder to write compilers, because you’re always trying to get around the guff in the design.

      That’s why the compilers are so bad, why there’s no proper Gcc for it (unlike MSP, Cortex and AVR8).

      1. I agree. I started programming microcontrollers with PIC16/PIC18, I’ve written alot of code for these chips (the majority in assembly). I wasn’t aware of how bad the PIC16 architeture is before I’ve learned the AVR8 internals. Have you ever tried to write a boot loader for PIC16? It’s an ugly hack that has side effects in the firmware that you will upload through the boot loader.

        Today I’m using ARMs to get the job done, but I think 8 bit micros are much easier to start. One thing I like from PIC micros is that it has an excellent docmentation.

      2. I’m not getting this
        PIC – RISC
        AVR _RISC
        PIC – Harvard architecture
        AVR – Harvard architecture

        I thought register layout was much the same for all RISC’s – that is a bank of registers and usually one that can be used as an index to the others. Basically flat SRAM with just about every register can be used for whatever purpose.

        There are also AVR’s that you can’t right a bootloader for at all because of the Harvard architecture, you can’t write to instruction memory at all. Obviously there are exceptions.

        I don’t know the stack design of an AVR so yes, if you can’t extend the lousy stack of a PIC then yes that is very limiting.

        But apart from that (ie if the stack can be extended) then everything should be abstracted away.

        At the end of that. The PIC would still be better for some things and the AVR others, no level of abstraction will change that.

        1. Generally, PIC cripples its CPU extremely, to have more real estate on a chip die for peripherals. After all that is where they came from at first, making smarts in smart peripherals. That’s why they are pricing champions when it comes to categories of features per buck, and in cheapest of tiniest of MCUs. So, for dumb, cheap chip, you need a smart compiler – the costs are shifted from the manufacturing into development, recurring expenses into non-recurring ones. It makes monetary sense for big players and it gives big players edge over (non-masochist) little guys.

        2. The PIC is not a RISC processor, it was just a hacky CPU designed by General Instruments in the 1970s to complement a minicomputer with slow I/O. Just because Microchip claim it’s RISC, doesn’t make it so.

          RISC computers are designed with the principle of ‘making the common case go fast and the uncommon case work’ and streamlining the design for the purpose of High level languages. To do this, RISC designs rely on these features:

          1. A large register file. PIC *doesn’t* have a large register file, it is architecturally equivalent to an accumulator architecture with RAM. RISC has large register files so that you can do fast register to register operations. A PIC on the other hand mustdo: movf VariableA,w then say, addwf VariableB, then movwf VariableC. Everything goes through w, it’s an accumulator architecture. AVR on the other *does* have a large, 32 register, register file.

          2. An indexed addressing mode. PIC has a direct addressing mode and an indirect addressing mode accessed via I/O registers. Therefore, high-level constructs like records or stacks are a pain to support. Hence the typical need to work out the entire call-tree of a 8-bit PIC application and banning recursion. The AVR supports only simple addressing modes indexed addressing and post-incremented/pre-decremented addressing.

          3. At least two register fields for ALU operations. The PIC is an accumulator architecture and doesn’t support ALUop srcReg,dstReg. RISC architectures will do this for two or *three* operand instructions. The AVR support two-operand instructions of this kind, so it’s somewhat RISC in that sense.

          4. General purpose registers. Well, since the 8-bit PIC only has an accumulator, it’s out from the start. Worse still, what passes for an address register is just an I/O location. The AVR has several classes of registers, which means it’s not truly RISC: half of its registers can’t handle immediate operations; only 3 register pairs can be used as pointers (and X can’t do indexing, ironically); only 4 register pairs can support register pair ALU ops; multiplication and division will only work with limited registers (still, better than a PIC 18 though).

          5. Simple uniform instructions. PIC does at least do this.

          However, since it lacks most of the criteria for RISC, the PIC is an awkward architecture for compilers. It’s not RISC and in practice will be far slower instruction cycle for instruction cycle than an AVR.

  8. I still remember being in lab at 2AM, a few days before having to fly out to give a dem oto sponsors, desperately trying to get a piece of our network stack to work. After hours of stepping through and auditing code, I found:
    //#define TRUE 1
    #define TRUE 0
    #define FALSE 0

    Someone had disabled another part of the network stack by redefining what “TRUE” was, rather than change it to FALSE in the code. And that guy now works at Nest …. but I am confident that my rant to him for that poor coding practice was angry enough that he’ll never do something like that again.

      1. I think this guy deserves a prize!

        Why would you want to #define a value (or enum or const or whatever) to a constant value that apparently needs to be constant, ever, otherwise everything stops working!

        That’s the price to pay for not having a true (pun intended…) boolean type in C for years and years. If you’re not going to use bools or enums but insist to use ints with only two values, why not just use these two values. No use nor sense in #define’ing 1 to TRUE and 0 to FALSE then, you KNOW they must be that way or it won’t work.

        Or just use proper bools and you don’t have this problem either.

        Just another case where #define proved to be harmful.

      2. Actually, you would want #define TRUE !FALSE since FALSE always has the value 0 while any “not 0” value is accepted as TRUE. That is why one should always compare to FALSE instead of TRUE. There are some compilers out in the wild that don’t constrain the result of test operations to 0 or 1.

  9. An additional caveat with regard to inline functions. The compiler is not required to respect an inline keyword. As such there is no guarantee that the function will be inlined. In fact, most cases it wont.

    1. Don’t try to be in control over everything! Microprocessors have evolved to a stage where a human can no longer predict every aspect of execution. If the compiler thinks it is better to not inline the function, why would that be a problem! Use optimisation flags to instruct the compiler for small or fast code, that should be enough.

      Several computer science “gurus” have told us many times. If you want to optimise code: don’t do it! If you still want to do it: still don’t do it! And yet if there is still urge left, at least do it at the very final stage, don’t let it influence the process of correctly producing code.

    1. C macros *can* do looping! You just haven’t abused them hard enough! Here’s one of the examples, “99 bottles of beer on the wall”, from a project that implements a functional language with the C preprocessor:

      Here’s another one that calculates the value of the 500th fibonacci number with the preprocessor:

      In fact, unrolling loops? It just so happens they have an example of that, too!

      If you want a better look at *how* weird things can be achieved with the C preprocessor, you can have a look at:,-tips,-and-idioms
      IIRC the method of looping described in the Cloak wiki has a relatively low loop limit due to the design; the actual code actually contains a somewhat better method, the description just hasn’t been updated. (Not sure about the limits of that one) But I’m convinced the order-pp looping method at least is pretty powerful, although I haven’t really delved into the internals of order-pp that much.

  10. I would have thought the following would be a beginners trap for DEFINE vs CONST in the Arduino IDE.

    const char message[] = “hello world”;

    DEFINE MESSAGE Hello World


    ie with DEFINE you have to force type setting or conversion as it is not otherwise set like it is with const

    so you have to have quotes around MESSAGE for force conversion
    Some times you need something like –
    myVar = int(DEFINED_VAR);

      1. It’s not the coder lol. The assembler obliviously knows what a string literal is but you can’t say that the pre-processor knows what a string literal is because according to the pre-processor *everything* is a string literal.

        The Arduino will BUG out with some DEFINE’s because they don’t specify a type – yes there is *some* type conversion but the IDE will sometimes spit the dummy when it doesn’t know what the source *type* is.

        From memory (vaguely) you can –
        int x = 1;
        digitalWrite(10, x);
        if 1 has been typecast to numeric because it is easily converted to HIGH / LOW
        #DEFINE X 1
        digitalWrite(10, X);
        won’t work because the typeOf X is unknown.
        This can be fixed with –
        #DEFINE X 1
        digitalWrite(10, int(X));
        even though type -‘int’ is wrong for a digital pin but as it’s type is *known* it can be cast.

        That is what the Arduino web site promotes the use of
        const int x = 1;
        over #DEFINE because const always includes a type definition.

        1. Seems like Arduino IDE is broken in yet another place. Correct me if I’m wrong, but from my understanding, by the time pre-processor is done with the code

          #DEFINE X 1


          should result in the exact same code passed to the compiler for error checking.

        2. It’s #define, not #DEFINE

          Serial.println(“MESSAGE”); prints the text MESSAGE to the serial monitor.

          #DEFINE X 1
          digitalWrite(10, X);
          works fine if you change #DEFINE to #define. In fact if you look at line 40 of Arduino.h:
          #define HIGH 0x1

    1. Followed the link above and there is an error in your code:

      int LEDPIN = 1;
      void main(void)
      while (1) {
      PINB |= (1<<LEDPIN);

      This would not blink the LED as the pin is always set high, it should be an XOR operator:

      PINB ^=(1<<LEDPIN);

  11. Like the rest of c, the preprocessor is a tool. The tool user has to exercise discipline or the tool will bite. My history using the cpp in large embedded systems (~70k lines of code) has had it generating massive tables of information (did you know you can include the same file multiple times?) about bits (who would have dreamed a bitinfo macro would have 7 parameters to describe a single bit?), busses, state machines, displays, etc. to help prevent programmer errors. To prevent typos, it allowed using the same include file both for allocation and references across hundreds of routines. The cpp made keeping track of code for dozens, if not hundreds, of change requests manageable when generating a myriad of code images.Like enum, static, and const, #define, #ifdef, #endif, #,,##, #include, and #undef can all be good tools but practice and discipline help. YMMV since not all programmers will take the time to learn how to use the tools or want to exercise discretion. It works for me.

  12. I really could not care less. You should not use that screwdriver for that purpose.. Well great, now off you fuck and make something worthwhile with all the stuff you are not using…

  13. “Remember, an inline function does not produce a function call since it is inserted directly into the surrounding code.”

    This incorrect statement mostly sums up the article for me, if you don’t know the language well enough (as the author apparently doesn’t), you’ll make mistakes, i see the #define stuff and it shows terrible examples designed to fail, but they’re usually based on a premise that would fail elsewhere, using () to control how an expression is evaluated is a 101 of the language, and it would be applied elsewhere even in basic mathematics. Being aware of it makes you a better programmer.

    If the language has improved and added features that make other features obsolete, used the, but also check what they do first.

    Sometimes the issue are, as noted, is the implementation of the compiler. It doesn’t matter what the creator intended, its what the implementer implemented. .

    learn, code, test , measure performance where it matters, understand what the underlying compiler does. Don’t take articles such as this as golden and test for yourself.

    1. One of the things I have done for 8-bit not so optimized C or when I first learn VHDL/Verilog compiler is to get a feel on what low level output it is compiled to (asm for microcontroller or the low level library at EDIF/schematic/HDL). I tune my coding style accordingly to get the code I want. e.g. I have managed to write state machines using verilog/VHDL for tiny PAL chips and get same level control as PALASM.

      For larger/faster processors or a more professional C compiler, I’ll skip that step.

      1. talk about necro’ing a post/comment. but oddly i came across the article googling something else re-read and thought the same. simply that inline is a suggestion that the compiler can ignore at compile time, its not a guarantee, a define can’t be ignored and its gone by the time it hits the compiler.

        people talk about const/inlines replacing defines but they’re aren’t equivalent it is also a lot harder for a compiler to optimise away a const than an a define for instance. a const isn’t the same in the sense of it means read only, vs can’t ever change. magic number defines can’t ever be changed.

  14. To me, it’s uninteresting to talk about where using #define is bad. It’s not like there isn’t any history to guide us experience to draw from. What’s more interesting is to talk about where #define is good (or at least necessary). Here’s one:

    Back in 2000, Lua didn’t have bitwise operators and I needed them for my work. So I added a bit-twiddling library (this is no longer necessary as Lua now has bitwise operators). When you think about implementing such a library in C, you realize there is some boilerplate code– get the first and second arguments, convert to integer, perform the operation, and return the result. The only difference between most of the bitwise operators is the operation you’re performing. So #define to the rescue! No, this technique isn’t necessary and may be considered preprocessor abuse, but it shows that #define gives a (very) crude metaprogramming mechanism to play with.

    And here’s another one:

    This is probably more clearly preprocessor abuse. But it’s also useful.

  15. #define’s are use by people that think compiler warnings are just a nuisance and that, apparently, prefer to the debugging afterwards. No wonder so much software is just crappie nowadays. Edsger already knew it 30 years ago. Now only the rest of the world. But somehow programmers tend to be exceptionally stubborn.

  16. #defines are used by people:

    – that don’t care about readability of their program by others
    – that program on machine code level and make the C-code such that the generated machine code more or less matches what they have in mind
    – think that compilers are dumb and warnings are a nuisance
    – it only counts how quick the code runs (more or less), not whether it’s behaviour will be always predictable
    – tend be debugging afterwards

    This is all exactly opposite to Edsger Dijkstra idea’s of programming. And that’s why the quality of commercial software is even worse than thirty years ago when he already predicted this. The nowadays common practise of thorougly testing and debugging is very propable a horror to him.

    Have at least the compiler help you find oversights (and yes there WILL be such, overconfidence is unfortunately very common with programmers). Not using #defines will give the compiler more knowledge of what you meant to do (as will using properly typed variables, etc.) so it can give more meaningful warnings. Warnings are not a nuisance, they will really help you in the end.

    My teacher would use to say: “What does it mean if you find one bug in your code. What does it mean if you find five. How about 10? It means NOTHING. The code should be correct to start with. You won’t ever find ALL bugs. Debugging is a last resort, it’s not a good practise.”

      1. System include files must do stuff that the compiler not really has provisioning for. Also a lot of them have to do with conditional compilation. That’s a whole other thing, you can only do that with #define and #if. In your own code you should try to avoid anyway.

  17. Explain me this…
    Why is MULT(x,y) found in some code better than

    (x*y) found in the code..

    If I see MULT(x,y) I could assume it is multiply but I would have to go look it up..

    If I see x*y I know what it does.

    Go easy i am just a humble idiot.

    1. That was only an example; the actual operation could be much more complex. A typical gotcha is a ‘MIN’ macro:

      #define MIN(x, y) x<y?x:y

      Won't work because x and y might be expressions, giving the wrong expansion. So you parenthesize them:

      #define MIN(x, y) (x)<(y)?(x):(y)

      But there's also the issue of using MIN in another expression, where order of operations interferes with your ternary expression, so let's parenthesize that too:

      #define MIN(x, y) ((x)<(y)?(x):(y))

      But even now, there's the issue mentioned in the original post: What if you call MIN(a++, b)?

    2. Exactly! If one would define (pun intended…) a static inline function min(int x, int y), it would all be clear and there would be no surprises. If it’s not used in a time-critical section, don’t make it inline, even.

      1. I use inline for a lot of one or two line functions to get clutter out of the way. I’d create pinSet(13) and pinReset(13) to replace pinWrite(13, 1) and pinWrite(13,0). Or even create ledSet / ledReset if I’m just working with pin 13. It saves typing if I use it a lot and clarifies what is taking place. Not the best example. I’ve hidden some much more gnarly code in inline functions.

    3. You may be humble but not an idiot. You asked a reasonable question. I used it simply to illustrate the problems using macros. Back in the early C days you might have created a macro for multiplying complex numbers. That’s still a bit of a reach but possible.

  18. I agree, mostly, with everything said in Rud’s article. I think that the old addage, “Even if you make something foolproof, nature will produce a better fool.”, sums it all up. Originally, M4 was the preferred C preprocessor. Think about how horribly that would allow one to screw up code. C++ is still a living and growing (improving?) language. C seems to have stagnated after the C99 specification. And, there are still way too many ambiguities. Compilers can decide whether to honor inline, or not. The type of an enum is decided by the compiler, including whether it is signed or unsigned. And, since day 1, there has been the long type. Even well vetted projects, like Linux and Python, are full of places where it is assumed that sizeof long and sizeof void_pointer are the same. Then there are things like Grub, where the author states that it is written in GCC, not C or C++. And, then does things like nested function definitions which GCC will let you get away with.

    The most popular C compiler available today is stuck at the C95 (ANSII C with addendum 1) level, but still isn’t standards compliant unless you constrain it to ANSII (C89).

    I think the only solution is to develop or select a coding standard, publish it, then enforce it. It won’t help globally, but will at least set up pools of uniformity.

    Even then though, a better fool will come along and do something that sends us all screaming into the darkness.

  19. ‘bad coding’ is not just related to macro use, one can have bad coding in any aspect of a program. I kinda like macros because they make my programs easier to read and more structured.
    However, sometimes I come across situations that macros solve or avoid a problem. In reading an RTC I once used #define DS3231_ADDRESS 0x68; (which is not proper use for a macro, but i got it from example code)
    Which worked perfectly, whereas:
    const byte DS3231 = 0x68;
    generated a ‘call to member function is ambiguous’ error.
    i still dont know why and eventhough 0x68 fits in a byte, const int DS3231 = 0x68; fixed it too but at least the macro avoided it

    1. Macro’s are bad anyway, no matter what. They add an extra layer between YOU and the compiler where all kinds of badness, mistakes etc. can happen. You never know what the compiler gets to see.

      Just use static const’s, enums or static inline functions instead and get rid of the cruft.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.