Are they evil? Maybe. The problem with globals is that they may be accessed and modified at any point in time by any function or piece of code being executed, without restrictions. This may lead to situations that are, let's say, difficult to trace back and explain. Minimizing the amount of globals, if possible bringing the amount back to zero, is therefore desirable.
Can they be avoided? Almost always yes. The problem with Arduino is, that they force you into this two function approach in which they assume you setup()
and you loop()
. In this particular case you have no access to the scope of the caller function of these two functions (probably main()
). If you had, you would be able to rid yourself of all globals and use locals instead.
Picture the following:
int main() {
setup();
while (true) {
loop();
}
return 0;
}
This is probably more or less what the main function of an Arduino program looks like. Variables you need in both the setup()
and the loop()
function would then preferably be declared inside the scope of the main()
function rather than the global scope. They could then be made accessible to the two other functions by means of passing them as arguments (using pointers if necessary).
For example:
int main() {
int myVariable = 0;
setup(&myVariable);
while (true) {
loop(&myVariable);
}
return 0;
}
Note that in this case you also need to change the signature of both functions.
As this might not be feasible nor desirable, I see really only one way to remove most globals from an Arduino program without modifying the forced upon program structure.
If I recall correctly, you're perfectly able to use C++ while programming for Arduino, rather than C. If you're not familiar (yet) with OOP (Object Oriented Programming) or C++, it might take some getting used to and some reading.
My proposal would be to create a Program class and create a single global instance of this class. A class should be considered the blueprint for objects.
Consider the following example program:
class Program {
public:
Program();
void setup();
void loop();
private:
int myFirstSampleVariable;
int mySecondSampleVariable;
};
Program::Program() :
myFirstSampleVariable(0),
mySecondSampleVariable(0)
{
}
void Program::setup() {
// your setup code goes here
}
void Program::loop() {
// your loop code goes here
}
Program program; // your single global
void setup() {
program.setup();
}
void loop() {
program.loop();
}
Voilà, we've rid ourselves of almost all globals. The functions in which you would start adding your application logic would be the Program::setup()
and Program::loop()
functions. These functions have access to the instance specific member variables myFirstSampleVariable
and mySecondSampleVariable
whereas the traditional setup()
and loop()
functions do not have access as these variables have been marked class private. This concept is called data encapsulation or data hiding.
Teaching you OOP and/or C++ is a bit out of scope of the answer to this question so I'll stop here.
To summarize: globals should be avoided and it's almost always possible to drastically reduce the amount of globals. Also when you're programming for Arduino.
Most importantly I hope my answer is somewhat useful to you :)