23

I'm relatively new at programming and many of the coding best practices I'm reading effectively state that there are very few good reasons to use a global variable (or that the best code has no globals at all).

I've done my best to keep this in mind, when writing software to make an Arduino interface with an SD card, talk to a computer and run a motor controller.

I currently have 46 globals for about 1100 lines of "beginner level," code (no line having more than one action). Is this a good ratio or should I look at reducing it more? Also what practices can I employ to reduce the number of globals further?

I'm asking this here because I'm specifically concerned with best practices for coding on Arduino products rather than computer programming in general.

  • 2
    In Arduino you can't avoid global variables. Every variable declaration outside the scope of a function/method is global. So, if you need to share values between functions, they have to be globals, unless you want to pass every value as an argument. – user31481 Jul 11 '17 at 13:35
  • 16
    @LookAlterno Err, can you not write classes in Ardunio, since it's just C++ with weird macros and libraries? If so, not every variable is global. And even in C, it's usually considered best practice to prefer passing variables (perhaps inside structs) into functions rather than having global variables. Might be less convenient for a small program but it usually pays off as the program gets larger and more complex. – Muzer Jul 11 '17 at 15:56
  • 11
    @LookAlterno: "I avoid" and "you can't" are very different things. – Lightness Races in Orbit Jul 11 '17 at 20:58
  • 2
    Actually, some embedded programmers ban local variables and require global variables (or function scoped static variables) instead. When the programs are small, it can make it easier to analyze it as a simple state machine; because variables never overwrite each other (ie: as they do stack and heap allocated variables). – Rob Jul 12 '17 at 1:53
  • 1
    Static and global variables offer the advantage of knowing memory consumption at compile time. With an arduino having very limited memory available, this can be an advantage. It quite easy for a novice to exhaust the available memory and experience untraceable failures. – antipattern Jul 12 '17 at 14:24
32

They aren't evil per se, but they do tend to get overused where there is no good reason to use them.

There are plenty of times when global variables are advantageous. Especially since programming an Arduino is, under the hood, vastly different to programming a PC.

The biggest benefit to global variables is static allocation. Especially with large and complex variables such as class instances. Dynamic allocation (the use of new etc) is frowned upon due to lack of resources.

Also you don't get a single call tree like you do in a normal C program (single main() function calling other functions) - instead you effectively get two separate trees (setup() calling functions, then loop() calling functions), which means that sometimes global variables are the only way to achieve your goal (i.e., if you want to use it in both setup() and loop()).

So no, they're not evil, and on an Arduino they have more and better uses than on a PC.

  • Ok, What if it's something I'm only using in loop() (or in multiple functions called in loop())? would it be better to set them up in a different way than defining them at the beginning? – ATE-ENGE Jul 11 '17 at 13:27
  • 1
    In that case I would probably define them in loop() (maybe as static if I needed them to retain their value across iterations) and pass them through the function parameters down the call chain. – Majenko Jul 11 '17 at 13:49
  • 2
    That is one way, or pass it as a reference: void foo(int &var) { var = 4; } and foo(n); - n is now 4. – Majenko Jul 11 '17 at 14:09
  • 1
    Classes can be stack-allocated. Admittedly, it's not good to put large instances on the stack, but still. – JAB Jul 11 '17 at 18:06
  • 1
    @JAB Anything can be stack allocated. – Majenko Jul 11 '17 at 18:16
18

It's very difficult to give a definitive answer without seeing your actual code.

Global variables are not evil, and they often make sense in an embedded environment where you typically do a lot of hardware access. You have only four UARTS, only one I2C port, etc. So it makes sense to use globals for variables tied to specific hardware resources. And indeed, the Arduino core library does that: Serial, Serial1, etc. are global variables. Also, a variable representing the global state of the program is typically a global.

I currently have 46 globals for about 1100 lines of [code]. Is this a good ratio [...]

Is not about the numbers. The right question you should ask yourself is, for each of these globals, whether it makes sense to have it in global scope.

Still, 46 global seems a bit high to me. If some of these hold constant values, qualify them as const: the compiler will usually optimize out their storage. If any of those variables is only used inside a single function, make it local. If you want its value to persist between calls to the function, qualify it as static. You could also reduce the number of “visible” globals by grouping variables together inside an class, and having one global instance of this class. But only do so when it makes sense to put stuff together. Making a big GlobalStuff class for the sake of having only one global variable will not help make your code clearer.

  • 1
    Ok! I didn't know about static if I have a variable that is used only in one function but get a new value every time a function is called (like var=millis() ) should I make that static ? – ATE-ENGE Jul 11 '17 at 13:37
  • 3
    No. static is only for when you need to keep the value. If the first thing you do in the function is set the value of the variable to the current time there is no need to keep the old value. All that static does in that situation is wasting memory. – Andrew Jul 11 '17 at 13:50
  • This is a very well-rounded answer, expressing both points in favour of, and scepticism about, globals. +1 – underscore_d Jul 11 '17 at 22:50
6

The main issue with global variables is code maintenance. When reading a line of code, it is easy to find declaration of variables passed as parameter or declared locally. It is not so easy to find declaration of global variables (often it requires and IDE).

When you have many global variables (40 is already a lot), it becomes difficult to have an explicit name that is not too long. Using namespace is a way to clarify role of global variables.

A poor way to mimic namespaces in C is:

static struct {
    int motor1, motor2;
    bool sensor;
} arm;

On intel or arm processor, access to global variables is slower than other variables. It is probably the opposite on arduino.

  • 2
    On AVR, globals are on RAM, whereas most non-static locals are allocated to CPU registers, which makes their access faster. – Edgar Bonet Jul 11 '17 at 15:21
  • 1
    The problem isn't really finding the declaration; being able to quickly understand the code is important but ultimately secondary to what it does - and there the real problem is finding which varmint in which unknown part of your code is able to use the global declaration to do weird things with an object you left out in the open for everyone to do whatever they want with. – underscore_d Jul 11 '17 at 22:51
5

Although I wouldn't use them when programming for a PC, for the Arduino they have some benefits. Most if it has already been said:

  • No dynamic memory usage (creating gaps in the limited heap space of an Arduino)
  • Available everywhere, thus no need to pass them as arguments (which costs stack space)

Also, in some cases, especially performance-wise it can be good to use global variables, instead of creating elements when needed:

  • To decrease gaps in heap space
  • Dynamically allocating and/or freeing memory can take substantial time
  • Variables can be 'reused', like a list of elements of a global list that are used for multiple reasons, e.g. once as a buffer for the SD, and later as a temporary string buffer.
5

As with everything (other than gotos which are truly evil) globals have their place.

e.g. If you have a debug serial port or a log file that you need be to able to write to from everywhere then it often makes sense to make it global. Similarly if you have some critical piece of system status information then making it global is often the easiest solution. There is no point in having a value that you have to pass to every single function in the program.

As others have said 46 seems a lot for only just over 1000 lines of code but without knowing the details of what you are doing it's hard to say if you are over using them or not.

However for every global ask yourself a few important questions:

Is the name clear and specific so that I don't accidentally try to use the same name somewhere else? If not change the name.

Does this need to ever change? If not then consider making it a const.

Does this need to be visible everywhere or is it only global so that the value is maintained between function calls? Is so consider making it local to the function and using the keyword static.

Will things really screw up badly if this gets changed by a piece of code when I'm not being careful? e.g. if you have two related variables, say name and ID number, that are global (see previous note on using global when you need some information almost everywhere) then if one of them gets changed without the other nasty things could happen. Yes you could just be careful but sometimes it's good to enforce carefulness a little. e.g put them in a different .c file and then define functions that set both of them at the same time and allow you to read them. You then only include the functions in the associated header file, that way the rest of your code can only access the variables through the defined functions and so can't mess things up. This is basically a pure c way of doing what a c++ class would allow but without having to learn how to define and create classes so you aren't learning too much at once.

-- update -- I just realized that you had asked about Arduino specific best practice rather than general coding and this is more of a general coding reply. But honestly there isn't much difference, good practice is good practice. The startup() and loop() structure of Arduino means that you have to use globals a little more than other platforms in some situations but that doesn't really change much, you always end up aiming for the best you can do within the limitations of the platform no matter what the platform is.

  • I know nothing about Arduinos but do a lot of desktop and server development. There is one acceptable usage (IMHO) for gotos and that's to break out of nested loops, it's much cleaner and easier to understand than the alternatives. – Persistence Jul 13 '17 at 11:44
  • If you think goto's are evil, check out Linux code. Arguably, they are just as evil as try...catch blocks when used as such. – Dmitry Grigoryev Jul 13 '17 at 12:59
5

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

  • You can define your own main() in the sketch if you prefer. This is what the stock one looks like: github.com/arduino/Arduino/blob/1.8.3/hardware/arduino/avr/… – per1234 Jul 12 '17 at 14:05
  • A singleton is effectively a global, just dressed up in an extra confusing way. It has the same downsides. – patstew Jul 12 '17 at 14:05
  • @patstew Do you mind explaining me how you feel it has the same downsides? In my opinion it doesn't since you're able to use data encapsulation to your benefit. – Arjen Jul 12 '17 at 15:23
  • @per1234 Thanks! I'm definitely no Arduino expert, but I suppose my first suggestion could work also then. – Arjen Jul 12 '17 at 15:28
  • 2
    Well, it's still global state that can be accessed anywhere in the program, you just access it through Program::instance().setup() instead of globalProgram.setup(). Putting related global variables into one class/struct/namespace can be beneficial, especially if they're only needed by a couple of related functions, but the singleton pattern doesn't add anything. In other words static Program p; has global storage and static Program& instance() has global access, which amounts to the same as simply Program globalProgram;. – patstew Jul 12 '17 at 15:32
4

Global variables are never evil. A general rule against them is just a crutch to let you survive long enough to gain the experience to make better decisions.

What a global variable is, is an inherent assumption that there is only one of a thing (it doesn't matter if we're talking about a global array or map that might contain multiple things, that still contains the assumption that there is only one such list or mapping, and not multiple independent ones).

So before you make use of a global, you want to ask yourself: is it conceivable that I will ever want to use more than one of this thing? If it turns out to be true down the line, you will have to modify the code to un-globalize that thing, and you'll probably find out along the way that other parts of your code depend on that assumption of uniqueness, so you'll have to fix them as well, and the process becomes tedious and error-prone. "Don't use globals" is taught because usually it's a pretty small cost to avoid globals from the beginning, and it avoids the potential of having to pay a large cost later on.

But the simplifying assumptions that globals allow also make your code smaller, faster, and use less memory, because it doesn't have to pass around a notion of which thing it's using, doesn't have to do indirection, doesn't have to consider the possibility of the desired thing maybe not existing, etc. On embedded you're more likely to be constrained for code size and/or CPU time and/or memory than you are on a PC, so these savings can matter. And many embedded applications also have more rigidity in the requirements — you know that your chip only has one of a certain peripheral, the user can't just plug another one into a USB port or something.

Another common reason for wanting more than one of something that seems unique is testing — testing the interaction between two components is easier when you can just pass a testing instance of some component to a function, whereas trying to modify the behavior of a global component is a trickier proposition. But testing in the embedded world tends to be very different from elsewhere, so this may not apply to you. As far as I know, Arduino has no test culture whatsoever.

So go ahead and use globals when they seem worthwhile. The code police won't come and get you. Just know that the wrong choice could lead to a lot more work for you down the road, so if you're not sure...

0

Are Global Variables Evil in Arduino?

nothing is inherently evil, including global variables. I would characterize it as a "necessary evil" - it can make your life much easier but it should be approached with caution.

Also what practices can I employ to reduce the number of globals further?

use wrapper functions to access your global variables. so you at least are managing it from the scope perspective.

  • 3
    If you use wrapper functions to access global variables, you may as well put your variables inside these functions. – Dmitry Grigoryev Jul 13 '17 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.