I am working on one of my first projects in C (microcontroller code) which is split up into multiple C files. I noticed some inconsistency in my coding where I had some variables declared as extern global variables and would just access them directly (ISR and some other places), and others declared static global variables, and would then accesses them from different files using function calls.
What is the best way to do this? Should I always keep the scope as small as possible even if it means adding extra functions just to set or read the values? Or should I just make them global across all files and just access them directly.
1
I would say that in this specific case you would probably accomplish the same effect by declaring the variables as extern
. I’m basing this on the following assumptions:
- You do not have any logic within your getters/setters, and do not intend to add any.
- You have one declaration of
foo
, and functionsgetFoo()
andsetFoo()
. If you have different variables namedfoo
, with different getter/setter functions, than clearly you can’t make themextern
(but should probably rename them anyway).
That said, your code will be more modular (but no less coupled) if you adopt the practice of defining static
variables within the compilation unit, and access them via getters/setters.
5
The most important thing is to lay down an API and stick with it. C offers a few different ways to hide the implementation that look like functions to your callers, and that’s preferable to direct access because it gives you more avenues for changing the implementation without breaking existing code.
For example, you can start with variables and access disguised as functions by the preprocessor:
/* bar.h - Extern-Based Version */
extern int bar_foo;
#define bar_foo_get() (bar_foo)
#define bar_foo_set(x) bar_foo = (x)
Later, if you need bona fide functions with logic inside them, the interface remains the same:
/* bar.h - Function-Based Version. */
extern int bar_foo_get();
extern void bar_foo_set(int x);
You also have the option of writing your get
and set
functions as inline
. This gives you the best of both worlds by cutting down on overhead and providing better checking of calls at compile time. There can be some portability implications that can be overcome by knowing what they are and coding carefully around them.
Which method you choose really depends on whether or not your application can tolerate the extra overhead of a function call.