#define verse constant declarations

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

#define verse constant declarations

6,210 Views
mjcoury
Contributor I
Is there a benefit to using one or the other? I have always used #define but have read in O'reilly book that const is preferred for following scope rules... any feelings eitherway?
Labels (1)
0 Kudos
5 Replies

450 Views
J2MEJediMaster
Specialist I
Const is valuable because it informs the compiler that the "variable" that you're declaring isn't. This information can be used to:

1) Locate the constant value in firmware where it belongs instead of RAM, and

2) The optimizer, knowing that a variable is actually a constant value, can apply optimization techniques to reduce code size.

Just my two plutons worth.

---Tom
0 Kudos

450 Views
mjcoury
Contributor I
Yes that makes sense... am I correct in stating that a #define replaces values at the time of compile so would consume even less space since it would not require any storage at all?
0 Kudos

450 Views
rhinoceroshead
Contributor I


mjcoury wrote:
Yes that makes sense... am I correct in stating that a #define replaces values at the time of compile so would consume even less space since it would not require any storage at all?


Oh, it still requires storage.  It just stores it in Flash amongst the code.  The #define statement is a pre-compiler directive.  Technically, any line that begins with a # is something for the pre-compiler to act on.  The pre-compiler will replace all instances of the defined token with its definition.  So doing this:

#define DELAY 40
for (i=0;i<DELAY;i++) {
    for (j=0;j<DELAY;j++) {
        asm NOP;
    }
}

is exactly the same as this (as far as the compiler is concerned):

for (i=0;i<40;i++) {
    for (j=0;j<40;j++) {
        asm NOP;
    }
}

When the compiler generates machine code, it will see the number 40 and use the immediate addressing mode in order to compare with the accumulator.  The number 40 will be stored in the code as many times as you are referencing it.  In this case it is twice.  Here is the assembly generated by CodeWarrior Ver5:

Code:

    7:    char i,j;    8:    for (i=0;i<DELAY;i++) {  0002 95       [2]             TSX     0003 7f       [2]             CLR   ,X  0004          [5]     L4:         9:      for (j=0;j<DELAY;j++) {  0004 6f01     [3]             CLR   1,X  0006          [5]     L6:        10:        asm NOP;  0006 9d       [1]             NOP     0007 6c01     [4]             INC   1,X  0009 e601     [3]             LDA   1,X  000b a128     [2]             CMP   #40  ;<---- notice opcode a1 and immediate constant 40, which is $28 in hexadecimal  000d 25f7     [3]             BCS   L6  000f 7c       [3]             INC   ,X  0010 f6       [2]             LDA   ,X  0011 a128     [2]             CMP   #40  ;<---- and here it is again.  0013 25ef     [3]             BCS   L4   11:      }   12:    }   13:  }

 

It's really up to the compiler though, how to handle it.  I tried defining a 'const char' global variable and CodeWarrior generated exactly the same code as above.  So either way you do it, the compiler has the final say - but you can switch off certain optimizations if you want.

 

 

0 Kudos

450 Views
CompilerGuru
NXP Employee
NXP Employee
Actually there are three ways of defining such constants,
- #defines
- enums
- const variables.

There are pros and cons for all of them

#defines
Bad:
- does not follow scoping rules.
E.g.
a.h:
#define count 10
b.c
#include "c.h"
void fun(void) {
int count;
for (count = 0; count 7; count++)
...

-> fails suddently when one day someone includes a.h into b.c, for example by changing the unrelated c.h header.
Worse than this case, defines can also intruduce silently wrong behaviour.
- many common traps. Just one of them:
#define TWO 1+1
#define FOUR TWO*TWO
-> value of FOUR is 3 (or something else, depends on context)
- can be hard to debug as debugger cannot know them.
- no explicit types, dangerous, complex rules
#define FOURTY_THOUSAND (20000 + 20000)
#if FOURTY_THOUSAND != 40000
#error // no error
#endif
void Error(void);
void test(void) {
if (FOURTY_THOUSAND != 40000) {
Error(); // ERROR is called!!!
}
}

Good
- no memory is ever allocated
- it works when done right
- can be used in macro expressions.
- can be used in type expressions

- enums

Bad
- no explicit types (but still a lot cleaner than for defines)
- cannot be used in macro expressions.
- ugly syntax, introduces a new type to define a value.

Good
- follows scoping rules.
- can be used in type expressions
- no memory is ever allocated


- const int

Bad
- cannot be used in macro expressions.
- cannot be used in type expressions (allowed in C++, not allowed in C!)
- no memory is usually allocated, but possible (adr taken, switched off optimization,...)
- can cause problems when put in header files (in C), not allowed to define same constant twice.
- With extern const int: no optimization possible.

Good
- explict type, no surprises
- follows scoping rules


So in C, I'm usually using enums for simple numbers. Constants can be used in C++ or in C implementation files.
Using defines is advanced and dangerous, works when done right, but only then.

Daniel
0 Kudos

450 Views
Alban
Senior Contributor II
Hello,
Yes, you are correct, #DEFINE only replace the value at compilation.
As it's only copy of "text" as is, there is no impact of the space. It just saves you writting the same thing other and other again and allows to change a value easily impacting the whole software.
 
#DEFINE does not declare a variable.
Const does not declare a variable either on its own.
Using Const in conjunction with variable declaration puts it in ROM instead of RAM. (by default, see your PRM file in CW).
 
Cheers,
Alban.
0 Kudos