previous curse | all curses | next curse
Global variables often present an easy way how to hold a state that is assumed application-wise, so it can be accessed from every function. However, modern applications rarely use global variables since nothing is really application-wise and some level of encapsulation is usually advisable. In the Arduino projects, utilization of global variables will become a necessity since the interface is somewhat cringe. More advanced programmers may replace global variables with static variables, but that is quite beyond the scope of this text.
The motivation is perhaps best illustrated by the associated meme on the right. Once something is global, it may be changed from anywhere and that could be hazardous and error-prone since it is difficult to track, where the variable is being used. Global variables also complicate code reusability as any function that uses a global function cannot be simply used in another project, unless the global variable is copied as well. Finally, in C/C++, the global variables are even more dangerous since they can be used in functions without explicit declarations, so it is easy to mistake a global variable for a local variable.
In regular C/C++ applications, global variables are often avoided altogether. Data that needs to be shared across functions or modules are wrapped into structures or objects, allocated dynamically, and managed by smart pointers. Even without objects, we could make the variables local in main()
(unless they are large and require dynamic allocation) and pass them down as needed into called functions.
In Arduino code, this is not possible due to the interface which comprises setup()
and loop()
. Since the loop()
function is called repeatedly and should not block, the application state needs to be stored in global variables. However, we place some restrictions on the use of global variables:
setup()
and loop()
. There might be good exceptions to that (e.g., if you place part of the code from loop()
to a separate function which is not universal, but depends on the global state as well). However, it is always best practice to design your functions as independent as possible (preferably pure) and such functions must get all (or at least most) of their inputs via arguments.There is a quick test that would indicate whether a global variable in your code should be removed (replaced): If you could change the value of that variable in between two subsequent invocations of loop()
and it will not affect the behavior of your application, then the global variable should not be there.
Another good piece of advice is to minimize the number of global variables to make the situation more manageable. One way how to do that is to aggregate related values into structures/classes and then make one global instance of that structure.
The example is a bit artificial, but it demonstrates one of the most typical errors. The functions initialize()
and update()
are more universal when the state is passed in via an argument. When the state needs to be modified, it is passed via reference (&
). Subsequently, the variable (var
) could be either made local (in main()
) or accessed only from callers of initialize()
and update()
(which might be setup()
and loop()
in the case of Arduino).
💩 Bad code | 👍 Good code |
|
|