Const vs #define - When do you use them and why?

Arduino boards have microcontrollers with notoriously small amounts of RAM.  The Uno only has 2,048 bytes of static RAM available for your program to store variables.  So when you need to keep non-changing variables out of RAM which is best to use const or #define?

What is #define

#define is often misunderstood because it isn’t a programming statement.  Instead, it sets up a macro which causes a text replace to occur before code is compiled.  For example in this code:


#define pin 13
void setup() {
   pinMode(pin, OUTPUT);
}
void loop() {
  digitalWrite(pin, HIGH);
  delay(500);
   digitalWrite(pin, LOW);
   delay(500);
}

Before the IDE sends the code to the compiler, a pre-processor will go through and do a simple text-replace of all instances of the macro “pin” with the integer 13.  So that means the code actually compiled looks like this:

#define pin 13
void setup() {
   pinMode(13, OUTPUT);
}
void loop() {
  digitalWrite(13, HIGH);
  delay(500);
   digitalWrite(13, LOW);
   delay(500);
}

This is a very powerful and useful feature.  No RAM is consumed by a variable called “pin”.  In fact, no variable called “pin” exists at all!

What is const

The keyword “const” tells the compiler that a variable (or pointer) can not be modified.  However, it is still a variable and depending on how it is used in the code, may or may not consume RAM.  As it turns out, the compiler used by the IDE, avr-gcc, is smart enough to know that a variable with the const modifier can’t be changed within the active program and will try to leave it out of RAM.

So this code:

const int pin=13;
void setup() {
   pinMode(pin, OUTPUT);
}
void loop() {
  digitalWrite(pin, HIGH);
  delay(500);
   digitalWrite(pin, LOW);
   delay(500);
}

Will not actually consume any RAM.  The compiler knows there is no reason to create a variable in RAM, so it’ll stay out of RAM.

Using AVR-SIZE

You have probably noticed that when compiling code in the Arduino IDE, the status window will finish with a message like this:

binarysketchsize

The message “binary sketch size” refers to the size of the HEX file for the code, and has no (direct) relation to how much RAM will be used.

How do you know how much RAM is being used?  The avr-gcc toolchains comes with a utility called avr-size which breaks out how much static memory a program will use.  For each of the above code segments, the avr-size command returns the following information on each binary file.

Using #define:

avr-size -C sketch_nov11a.cpp.elf
AVR Memory Usage
----------------
Device: Unknown
Program: 4818 bytes
(.text + .data + .bootloader)
Data: 155 bytes
(.data + .bss + .noinit)

Using const:

avr-size -C sketch_nov11a.cpp.elf
AVR Memory Usage
----------------
Device: Unknown
Program: 4818 bytes
(.text + .data + .bootloader)
Data: 155 bytes
(.data + .bss + .noinit)

Shocking isn’t it?  Whether you use #define or const, the amount of Data memory used is the same: 155 bytes!

Which to use for “variables”:  #define or const?

This leads to the question of when should you use #define or const?

In this case variables, using #define can actually become a bad practice.  Why?  Because it can lead to very hard to find bugs.

Case against using #define for variables

For example, consider this code:

#define pin 13;
void setup() {
    pinMode(pin, INPUT);
}
void loop() {
    digitalWrite(pin, HIGH);
    delay(500);
    digitalWrite(pin, LOW);
    delay(500);
}

What happens when that code gets complied?  The code is turned into this:

#define pin 13;
void setup() {
    pinMode(13;, INPUT);
}
void loop() {
    digitalWrite(13;, HIGH);
    delay(500);
    digitalWrite(13;, LOW);
    delay(500);
}

See the problem?  Is the compiler giving us any clues?  Here’s the output from the compiler:

file included from sketch_apr10a.ino:2:
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/cores/arduino/Arduino.h:109: error: expected ',' or '...' before numeric constant
...
sketch_apr10a.ino: In function 'void setup()':
sketch_apr10a:3: error: expected `)' before ';' token
sketch_apr10a:3: error: expected primary-expression before ',' token
sketch_apr10a:3: error: expected `;' before ')' token
sketch_apr10a.ino: In function 'void loop()':
sketch_apr10a:6: error: expected `)' before ';' token
sketch_apr10a:6: error: expected primary-expression before ',' token
sketch_apr10a:6: error: expected `;' before ')' token
sketch_apr10a:7: error: expected `;' before ':' token
sketch_apr10a:8: error: expected primary-expression before ',' token
sketch_apr10a:8: error: expected `;' before ')' token

Conclusion

Since the avr-gcc compiler is smart enough to keep variables out of RAM when using the const keyword, it is better to just use that.  Eventually the “text-replace” nature of #define is going to bite you with a stray semicolon or other unexpected replace.

Reference

For in-depth information on this subject, check out this thread in the Arduino Forums:  Const vs #define - Arduino Forum.