What the difference? #define vs static global variable

I have always used the following method for years:

#define VALUE  5

setup()

{ 
       pinMode(VALUE,OUTPUT);

}

This is how Arduino always seems do it:

    int value = 5

setup()

{ 
       pinMode(value,OUTPUT);

}

I do understand that a variable can be dynamically modified and a definition cannot. But if the value does not get modified within the program, as with a pin assignment, why set it up as a variable? And the fact that as a definition cannot be accidently changed within the program, is a benefit in this case.

Also why does Arduino declare a port pin as an int when it has far less than 255 possible values? Wouldn't using unsigned char save a byte of program space? I know that sounds silly but using int for every variable can add up in wasted bytes. Boreland DOS C did the same thing, int as a default, but I always declare variables for what they are to hold bit length wise.

My understanding is that the preferred paradigm for Arduino code is to use

const byte value = 5;

This uses only 1 byte, and prevents modification.

2 Likes

Yes that is another option.

Are all these considered options? They all seem to work the same for me.

I believe the option in post#2 will not result in allocation of ram, as the constant is known at compile time and can be inserted directly in the code.
whereas, obviously, simply
byte value = 5;
must consume RAM, as there is no guarantee of no change.

  • Hum, was under the impression no bytes are used, but type checking is checked by the compiler.

  • Where are you software people ? :thinking:

2 Likes

Well, I could be wrong, but I believe the compiler has the option to optimize the code in that way, but that it may not be a guarantee that no RAM is used.

Otherwise, why not splurge, and use const long value = 5;?

Regardless, I may be wrong, and I'll happily cede to the "software people" when they arrive.

1 Like

there's no need to allocate RAM space for constants. The value is just replaced for the symbol when compiled

2 Likes

C++ still struggling with the legacy of decisions made in 1972...

Modern compilers are really good at optimizing away stuff that is not needed. That is good, because the programmer can specify the intent more clearly without worrying too much about bloat.

Ideally, the preprocessor should be sent to the dustbin of history, but unfortunately I have seen some compilers allocate RAM for "const int" so we have to be careful.

1 Like

Even so, that's an OK tradeoff for getting the strong typing and error checking of the compiler instead of the simple string substitution of the pre-processor.

Go with:

constexpr int value = 5;
2 Likes

Meh. The preprocessor does text substitution, which allows various useful behaviors that are not supported (or are very obscure) using just the C++ language constructs.

But for constants, const <type> var = val; is preferred.

I would be tempted to suggest the uint8_t type instead of int since this is what is used in the pin functions

But it’s not really documented as such as they only describe the pin parameter as being

the Arduino PIN number

Without specifying a type …

In any case, if the optimizer can see you use it always as a byte value in the API and as a read only then the variable won’t even exist

2 Likes

There are some serious differences between #define and const int

  1. A #define does not allow type checking where the const int does.
    The const int is therefore more robust / safe.
  2. a #define allows command line overriding of the value. E.g. if you want to define the size of a buffer a const int needs editing source code, while #define allows a -Dxx=yy parameter in command line.
  3. a #define is a preprocessor command and has purposes e.g. conditional coding for multiplatform support which cannot be done with a const int.

So I think best practices are to use const type when you need a constant number or string, and only use #define if you want it to be command line overridable.

A combination exist, to have a type checked, command line overridable constant.

#define CMD_LINE_ARRAY_SIZE    5
const int arrSize = CMD_LINE_ARRAY_SIZE;

Never used it and I use #defines quite a lot for constants, so mea culpa :slight_smile:

the preprocessor #ifdef is still useful to building code for different targets.

1 Like

True, but like many things, should be used in moderation, otherwise it leads to technical debt and maintenance nightmare. Less of a problem nowadays with CI, but even projects where there are just 2 targets I struggle to get devs to make changes without breaking the other target.

Then of course management ask "hey I thought the code could run on any target, so why can't we release just one firmware file to manufacturing which runs on all variants?"

so separate code bases for each target even though 90+% of the code is the same and will now require that identical changes be made to each target when there's a bug fix?

if you don't understand how to use the tool, don't use it. But don't condem it for the majority of people who do know how to properly use it

I condemn the tool, because it is an objectively crap solution.

you should have been in the lab when UNIX, C and the preprocessor were being developed

so you advocate having separate code based for each possible target.

can you "objectively" elaborate on that statement?

I don't like #define but they have their place - just have been somewhat abused.

1 Like

Really!
The few examples I looked at in the IDE use const int

1 Like

I was really referring to the preprocessor in general. #defines are ok for simple literals, but then you can use other (better) methods. Macros defining pure functions can be mostly replaced with inline functions. More "tricky" uses of macros should be avoided for obvious reasons.

It's all very well saying "it's ok if you are know what you are doing", I don't think I've ever met a developer who fully understands the language standard. Often those that think they do are surprised to find they rely on undefined behavior.

In 1972, the preprocessor was a neat trick. In 2025, not so much.