Const vs #define

Although #define is deprecated over const in the Arduino Reference Manual, and in C++ books is stated that there is little need for it, I note that its use is quite common in Arduino code. Why is this - particularly as there's no overhead in using const?

I ask because I was a confirmed #define user until I got bitten by a bug in a macro. It was such an unpleasant experience that I now avoid it and always use const..

Jim

Pre-processor directives such as #define existed in C long before the 'const' storage class. Old-school programmers go used to using #define for constants. You can also use an enum to create named constants.

It is possible that a 'const int' will take up memory space even if it not used. That would be a problem for libraries that define a large number of constants.

As preprocessor macros are pre-processed and not compiled, they themselves don't take up any memory / flash. Only their contents do.

So if a macro is not used then it won't take up any room. If it contains 1 byte then it will take up 1 byte per usage (depending on context).

const variables are real variables with real allocated space - whether they are used or not.

One thing that a const cannot do which a #define can is selective compilation and inline replacement.

#defines are absolutely perfect (and the only way) for making code that will compile on multiple platforms with different settings/features.

majenko:
const variables are real variables with real allocated space - whether they are used or not.

Please provide evidence or references.

Thanks for the replies.

I'm pretty sure that when I changed my code to use 'const ints' rather than '#defines', the compiled code wasn't any larger.

Jim

Please provide evidence or references.

http://www.nongnu.org/avr-libc/user-manual/pgmspace.html:

A Note On const

Many users bring up the idea of using C's keyword const as a means of declaring data to be in Program Space. Doing this would be an abuse of the intended meaning of the const keyword.

const is used to tell the compiler that the data is to be "read-only". It is used to help make it easier for the compiler to make certain transformations, or to help the compiler check for incorrect usage of those variables.

For example, the const keyword is commonly used in many functions as a modifier on the parameter type. This tells the compiler that the function will only use the parameter as read-only and will not modify the contents of the parameter variable.

const was intended for uses such as this, not as a means to identify where the data should be stored. If it were used as a means to define data storage, then it loses its correct meaning (changes its semantics) in other situations such as in the function parameter example.

for one.

Hmm, P138 of my 'Stroustrup' says with regards to macros:

"The first rule is: Don't use them if you don't have to."

and on P140:

"The const, inline and template mechanisms are intended as alternatives to many traditional uses of preprocessor constructs."

Seems pretty clear to me, which is why I asked why people are so 'gung ho' about #defines!

Jim

In a "traditional" (i.e., Princeton) architecture there's very little to chose between them.

However, with a microcontroller, with the Harvard architecture, using #define on a number guarantees that the number will end up in program space as it is literally just a number. Using "const" just makes it read-only and doesn't guarantee that it is in program space, thus taking up precious RAM when you don't need to.

However, if speed is what you want then const (in RAM) is faster that a number (in PROGMEM) as the PROGMEM will have flash wait states associated with it that you don't get with RAM.

It's all a matter of tradeoffs. Faster access + wasted memory, or slower access + more RAM available.

P138 of my 'Stroustrup' says with regards to macros

Does "macros" refer to all uses of #define? I would differentiate between using this:

#define LEDpin 13

which simply defines a manifest constant, and this (obviously fictitious) example:

#define READ_SOMETHING(a,b) { b = read(a); \
     C = b*something_else(); \
}

which can potentially cause problems if not used correctly, especially by someone other than the original author who's modifying/debugging the code later on.

Furthermore:

alternatives to many traditional uses

clearly implies that const is not a replacement for all such uses.

Pete

You make the claim the const variables always allocate space...

const variables are real variables with real allocated space - whether they are used or not.

As a reference you site compiler documentation that states that const variables may or may not allocate space...

const was intended for uses such as this, not as a means to identify where the data should be stored

I suggest you spend a bit more time searching.

Any potential optimisation is up to the compiler, isn't it? What does the Arduino IDE actually do with const variables (used or otherwise)?

majenko:
Using "const" just makes it read-only and doesn't guarantee that it is in program space,

True.

thus taking up precious RAM when you don't need to.

Only true for consts placed in SRAM. What about consts that are optimized into simple values?

However, if speed is what you want then const (in RAM) is faster that a number (in PROGMEM) as the PROGMEM will have flash wait states associated with it that you don't get with RAM.

Why are you bringing PROGMEM data into the discussion?

Why are you bringing PROGMEM data into the discussion?

Because that highlights a big distinction between const and #define.

A const may be in either RAM or PROGMEM. A value in a #define will always be in PROGMEM.

majenko:
A value in a #define will always be in PROGMEM.

Ah. There it is. The source of the confusion. You're misusing the word PROGMEM.

A #define of a simple constant will always be part of one or machine instructions stored in Flash. Which is also true for const declarations of simple constants. In the GCC-AVR world, they produce exactly the same code (a few machine instructions) and consume exactly the same amount of SRAM (zero).

const has the benefit of including a datatype. #define has the benefit of living in the preprocessor's world. Pick the one that suites the need.

PeterH:
Any potential optimisation is up to the compiler, isn't it?

Yes. And the linker.

What does the Arduino IDE actually do with const variables (used or otherwise)?

This...

  static const int MagicNumber = 42;

...and this...

  #define MagicNumber  ((int)(42))

...produce exactly the same code in all expressions. They are interchangeable.

You can exclude static if the symbol is not defined in any other modules (which is almost always the case).

The use of 'const int' (without the 'static') is what I've been using, and what I interpret is approved by the Arduino language reference. As I mentioned earlier, changing 'magic numbers' from #defines to consts gives exactly the same sized code in my (limited) experience.

Jim

I prefer const to #define as well.

The rules are (from memory so the accuracy is suspect)...

  • If the address is taken of a const, it becomes a first class variable (uses SRAM; slightly more code to access).
  • If a const is defined extern, it is treated as a first class variable.
  • Complex data-types (like arrays), are always first class variables.
  • If a const is defined in more than one module (more than one C or CPP file), it is treated as a first class variable. (without extern, this one may result in a linker error)

Compare:

const int ITEM_COUNT = 55;
const int MEANING_OF_LIFE = 42;

char buf [ITEM_COUNT];

void setup () 
{
  buf [5] = MEANING_OF_LIFE;
}

void loop () {}

Compiler:

Binary sketch size: 456 bytes (of a 32256 byte maximum)

Code size:

$ avr-size -C thefilename.elf

AVR Memory Usage
----------------
Device: Unknown

Program:     456 bytes
(.text + .data + .bootloader)

Data:         64 bytes
(.data + .bss + .noinit)

To:

#define ITEM_COUNT 55
#define MEANING_OF_LIFE 42

char buf [ITEM_COUNT];

void setup () 
{
  buf [5] = MEANING_OF_LIFE;
}

void loop () {}

Compiler:

Binary sketch size: 456 bytes (of a 32256 byte maximum)

Code size:

$ avr-size -C thefilename.elf

AVR Memory Usage
----------------
Device: Unknown

Program:     456 bytes
(.text + .data + .bootloader)

Data:         64 bytes
(.data + .bss + .noinit)

So it's the same.

The use of #define can lead to subtle problems, just as an example:

#define FOO 5
#define ITEM_COUNT FOO + 3

Now ITEM_COUNT should be 8, right? But if you later use:

char buf [ITEM_COUNT * 2];

That will expand to:

char buf [5 + 3 * 2];

So buf will be 11 long, not 16. Proof:

#define FOO 5
#define ITEM_COUNT FOO + 3

char buf [ITEM_COUNT * 2];

void setup () 
{
Serial.begin (115200);
Serial.println ();
Serial.println (sizeof buf);
}

void loop () {}

Output:

11

Also see here:

http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

In short, const identifiers act like they're part of the language because they are part of the language. The preprocessor can be thought of as a language layered on top of C++.

And read this bit:

Are you saying that the preprocessor is evil?

Yes, that's exactly what I'm saying: the preprocessor is evil.

Code:

#define FOO 5
#define ITEM_COUNT FOO + 3

Now ITEM_COUNT should be 8, right? But if you later use:

Code:

char buf [ITEM_COUNT * 2];

That will expand to:

Code:

char buf [5 + 3 * 2];

So buf will be 11 long, not 16.

Which is why you should ALWAYS add parentheses to macro arguments.
Macro Preprocessors 101.

I understand that. But it's why #define can be a trap. You wouldn't have the problem with:

const int FOO = 5;
const int ITEM_COUNT = FOO + 3;