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.


2 Answers 2


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 functions getFoo() and setFoo(). If you have different variables named foo, with different getter/setter functions, than clearly you can't make them extern (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.

  • Is there a point to getters and setters in a language without classes? Mar 26, 2013 at 16:51
  • @RobertHarvey - Same point as in a language with classes, IMO: it controls access to the variable. You can apply any validation logic, and also easily set a breakpoint if the variable is being changed when you don't expect it. And if you're lucky, your compiler will inline the function calls so that you don't pay anything for them.
    – parsifal
    Mar 26, 2013 at 17:07
  • Going a bit deeper, I think that a careful C programmer can gain many of the modularization benefits that are behind object-oriented programming. Of course, maintaining global state is not a way to get there.
    – parsifal
    Mar 26, 2013 at 17:10
  • @RobertHarvey: Validation logic can be difficult in C, since there's no exception-throwing mechanism to employ if validation fails. A more useful example in C might be update notifications. For example, in one version of a system, it may be possible to make something happen by writing a volatile variable (which e.g. gets polled in an interrupt routine that runs 60 times/second); a future version of the system, however, might require that code do something else to trigger that action (e.g. if it disables the interrupt when there's nothing for it to do, code which changes the variable...
    – supercat
    Jul 18, 2014 at 16:17
  • ...might have to also ensure that the interrupt is running).
    – supercat
    Jul 18, 2014 at 16:50

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.

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