Pages: 1 [2] 3   Go Down
Author Topic: Const vs #define  (Read 4672 times)
0 Members and 1 Guest are viewing this topic.
Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 1
Posts: 84
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Yes.  And the linker.

Quote
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
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12283
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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)
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Compare:

Code:
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:

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

Code size:

Code:
$ avr-size -C thefilename.elf

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

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

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

To:

Code:
#define ITEM_COUNT 55
#define MEANING_OF_LIFE 42

char buf [ITEM_COUNT];

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

void loop () {}

Compiler:

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

Code size:

Code:
$ 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:

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. Proof:

Code:
#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:

Code:
11

Also see here:

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

Quote
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:

Quote
Are you saying that the preprocessor is evil?

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

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 239
Posts: 24371
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
const int FOO = 5;
const int ITEM_COUNT = FOO + 3;
Logged

Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 1
Posts: 84
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

That's exactly why I'm sold on 'const'.

I'm no programmer so I need all the help I can get from preprocessor, without being caught by any 'gotchas' lying in wait!

Jim
Logged

Ontario
Offline Offline
God Member
*****
Karma: 20
Posts: 835
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This...

  static const int MagicNumber = 42;

...and this...

  #define MagicNumber  ((int)(42))

...produce exactly the same code in all expressions.

Try this one:

  foobar(&MagicNumber)
Logged

Dallas, Texas
Offline Offline
Sr. Member
****
Karma: 3
Posts: 267
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

For the code mostly used by arduino beginners, const should be the preference. And contrary to a previous assertion, conditional compilation is easily possible with the use of const.

Code:
#ifdef MYPACKAGE_FEATURE_ENABLED
const int optionalFeature = 10 ;
#endif

Furthermore, modern compilers are pretty good about dead code removal. There are some constructs which prevent this (as others have noted), but generally, global consts will simply be removed from the code when its never referenced - and in many cases even when it is.

Aside from that, the only other sticking point with const is multiple modules and global initialization. And even that can be avoided with proper semantics. Const is not perfect and not always free, but it should be the first trick in the bag unless you know you require a different solution. And as Nick pointed out, you are richly rewarded with the use of const.

const identifiers are often better than #define because:
Code:
   they obey the language's scoping rules
    you can see them in the debugger
    you can take their address if you need to
    you can pass them by const-reference if you need to
    they don't create new "keywords" in your program.

Lastly, remember "static" can effect const in surprising ways depending on context as the rules of static varies greatly depending on context. This in turn can effect code generation.
« Last Edit: January 13, 2012, 08:59:55 am by gerg » Logged


Ontario
Offline Offline
God Member
*****
Karma: 20
Posts: 835
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

For the code mostly used by arduino beginners, const should be the preference.

Agreed.  This is a go-nowhere thread from a beginner's perspective.

Quote
And contrary to a previous assertion, conditional compilation is easily possible with the use of const.

Code:
#ifdef MYPACKAGE_FEATURE_ENABLED
const int optionalFeature = 10 ;
#endif

Huh?  Show me a const definition of MYPACKAGE_FEATURE_ENABLED that would cause the code to be compiled.
Here you need a #define (or -D)
Logged

Dallas, Texas
Offline Offline
Sr. Member
****
Karma: 3
Posts: 267
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

They are not mutually exclusive. The contention was, at least as I read it, the use of const excluded the possibility of conditional compilation of const variables. That clearly shows that conditional compilation is not an issue in the least with const variables. And case in point, conditional compilation is one the primary reasons to continue to use pre-processor declarations.
Logged


Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 239
Posts: 24371
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Lastly, remember "static" can effect const in surprising ways depending on context as the rules of static varies greatly depending on context. This in turn can effect code generation.
sp. "affect"
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12283
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
...produce exactly the same code for all valid C++ expressions.

 smiley-wink

Quote
Try this one:  foobar(&MagicNumber)

Assuming you meant foobar to be this...

void foobar1( const int* mn );

Two possible outcomes...

1. Works as expected.
2. error: lvalue required as unary '&' operand
Logged

Ontario
Offline Offline
God Member
*****
Karma: 20
Posts: 835
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That clearly shows that conditional compilation is not an issue in the least with const variables.

A const value (it is not a variable) cannot be used as a basis for conditional compilation.  A construct like

Code:
const int condition = 1;

#if condition
  some-conditional-code;
#endif

is not possible.  Preprocessor conditional tests cannot "see" the value of a C/C++ const.  And C/C++ has no similar conditional compilation feature than can.  You simply cannot do most of the CPP features with const values.  You must use #define macros.
Logged

Ontario
Offline Offline
God Member
*****
Karma: 20
Posts: 835
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

produce exactly the same code

Quote
Two possible outcomes...

You take my point then?  There are things you can do with one, that you cannot do with the other -- they are NOT freely interchangeable.
Logged

0
Offline Offline
God Member
*****
Karma: 1
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

for the record...

In Keypad library:

Code:
typedef enum {IDLE, PRESSED, HOLD, RELEASED} KeyState;  // KeyState was KeypadState
Logged

Pages: 1 [2] 3   Go Up
Jump to: