#define VALUE vs. const int value

In my meanderings, I get mixed messages about the relative merits of #define vs. const for declaring things like pins and constant values used throughout a sketch. Some people say use the one, some say the other.

I have a fairly complex sketch (about 3000 lines) that runs a number of valves, motors, heaters and sensors, and I used a lot of defined values for things that I may want to change at some point, but don't need to be changed in the sketch itself.

As I understand it, defined values are only used at compile time, and don't take up any memory in the sketch, whereas const values do. Obviously, I'm missing something, because on the face of it, it seems like a no-brainer to save precious memory space. My naming convention is to use all caps for defined values, and bumpy-case for variables in the sketch so I don't get the two confused, which is the only obvious problem I see with defined values.

What are the more experienced members thoughts on this? TIA.

1 Like

The use of memory is a confused issue as if no memory was used, the value could not exist. The reality is one of which memory is used. #define'd values are embedded in the program and stored in FLASH. During compilation, the pre-processor replaces each instance of the identifier with the value. The const int will find a home in SRAM and during compilation will have it's identifier replaced by a pointer to the address. As pointers on a machine that has 16-bit addressing will be 2 bytes wide, the differences by using #define and const int are negligible, just in access time (SRAM is faster). Tou can see however, that if your #define'd value were a byte, your code would compile smaller and then larger if it were a long.

Another issue that comes into play is that some library functions require constant parameters so addresses can be assigned at the time of compilation. This dictates the use of #define as the compiler does not recognize a value stored in SRAM to be constant, despite it's naming.

One final 'behind the green door' difference is that you can actually change the value of a const.

1 Like

As I understand it, defined values are only used at compile time, and don't take up any memory in the sketch, whereas const values do.

"const" values will not usually take up any memory (especially if you use "static const"), thanks to optimization at compile-time.
Personally, I prefer "#define", because it's more flexible in the syntax (which is one of the things that can get you intro trouble.) And probably because I'm an old timer. For example, consider the following:

// A single clock tick is 4ms
const int32_t TICKTIME=4;
// compute number of ticks in standard time units.
const int32_t ONESEC=(1000L/TICKTIME);
   // vs
#define TICKTIME 4
#define ONESEC (1000L/TICKTIME)

(The "const" version doesn't work!)

Thanks guys! It doesn't seem like a clear advantage either way, performance-wise, and is more of a personal preference. A recurring problem I have is that every time I think I understand how something works and start counting on what I think I know, it turns out that either I am mistaken, or there is more to the story, LOL

If I understand correctly, to use an example: #define REFLOW_HEATER_1 12

At compile-time, the compiler will not allocate any specific memory to reflow heater 1, but simply substitute the value 12 for every instance of REFLOW_HEATER_1 in the sketch, whereas if I do: const int REFLOW_HEATER_1 = 12;

as a global variable, the compiler causes the sketch to allocate 2 bytes of memory at run-time, and at every instance REFLOW_HEATER_1 is called, it has to read those 2 bytes, and write their value to another memory location. If that is correct, it would seem to indicate a clear advantage to a #defined value vs. a const value, although at 16 million instructions per second, the real world difference is probably not that great.

The reason I like #defines is that I can group all the related definitions together at the top of the page, making them easy to find and modify as the situation calls for, whereas if I lump them under the global variables section, it gets messy, and seems to go against the custom of keeping global variables as few as possible. I'm still working on that, and have made all the original globals that are only used in one function locals, but that is another topic.

I'm new enough at this programming thing to want to keep things as simple and obvious as possible, even at a slight performance hit. I'd really like to learn the proper use of pointer variables, and that is my next priority this winter once everything is shut down.

JohnDeere630:
If I understand correctly, to use an example: #define REFLOW_HEATER_1 12

At compile-time, the compiler will not allocate any specific memory to reflow heater 1, but simply substitute the value 12 for every instance of REFLOW_HEATER_1 in the sketch, whereas if I do: const int REFLOW_HEATER_1 = 12;

as a global variable, the compiler causes the sketch to allocate 2 bytes of memory at run-time, and at every instance REFLOW_HEATER_1 is called, it has to read those 2 bytes, and write their value to another memory location. If that is correct, it would seem to indicate a clear advantage to a #defined value vs. a const value, although at 16 million instructions per second, the real world difference is probably not that great.

Whether it's macro substitution, or a const globally scoped variable, the data still has to be in the compiled binary. The compiler is smart enough to use similar optimization techniques on globally scoped const variables and numeric literals - though I'm sure that someone sitting there changing little things about a sketch could find cases where the behavior wasn't the same. In a few cases, it can be possible to generate code that doesn't have a separate place where the number in question is stored (there are several instructions where small operands are stuffed into the opcode) - but again, whether it's a constant variable or macro or numeric literal doesn't change this.

The macro substitution happens by the preprocessor, before the preprocessed source is passed to the compiler. It's not done at runtime (this is also true of const variables, as noted above. The compiler is pretty clever about optimizing). Macro substitution is a lot like running find/replace on your code immediately prior to each time you compile it. Other preprocessor directives like #ifdef and such behave similarly. This can be good and can be bad - among other things, you can use macros in ways you could never use variables or functions (you can pass a parameter to a macro too. F() is a macro, for example)