Electronic – Concept of the static keyword from the perspective of embedded C

cembeddedmicrocontrollerpic

static volatile unsigned char   PORTB   @ 0x06;

This is a line of code in a PIC microcontroller header file. The @ operator is used to store the PORTB value inside the address 0x06, which is a register inside the PIC controller that represents PORTB. Up to this point, I have a clear idea.

This line is declared as a global variable inside a header file (.h). So, from what I came to know about the C language, a "static global variable" is not visible to any other file – or, simply, static global variables / functions cannot be used outside of the current file.

Then, how can this keyword PORTB be visible to my main source file and many other header files which I created manually?

On my main source file, I only added the header file #include pic.h Does this have something to do with my question?

Best Answer

The keyword 'static' in C has two fundamentally different meanings.

Limiting Scope

In this context, 'static' pairs up with 'extern' to control the scope of a variable or function name. Static causes the variable or function name to be available only within a single compilation unit and only available to code that exists after the declaration/definition within the compilation unit text.

This limitation itself really only means something if and only if you have more than one compilation unit in your project. If you only have one compilation unit, then it still does things but those effects are mostly pointless (unless you like digging into object files to read what the compiler generated.)

As noted, this keyword in this context pairs with the keyword 'extern', which does the opposite -- by making the variable or function name linkable with the same name found in other compilation units. So you can look at 'static' as requiring the variable or name to be found within the current unit of compilation, while 'extern' permits cross-compilation unit linkage.

Static Lifetime

Static lifetime means that the variable exists throughout the duration of the program (however long that is.) When you use 'static' to declare/define a variable within a function, it means that the variable is created sometime prior to its first usage (which means, every time I've experienced it, that the variable is created before main() starts) and isn't destroyed afterwards. Not even when the function's execution is completed and it returns to its caller. And just like static lifetime variables declared outside of functions, they are initialized at the same moment -- before main() starts -- to a semantic zero (if no initialization is provided) or to a specified explicit value, if given.

This is different from 'auto' type function variables, which are created new (or, as-if new) each time the function is entered and then are destroyed (or, as-if they were destroyed) when the function exits.

Unlike the impact of applying 'static' on a variable definition outside of a function, which directly impacts its scope, declaring a function variable (within a function body, obviously) as 'static' has no impact on its scope. The scope is determined by the fact that it was defined within a function body. Static lifetime variables defined within functions have the same scope as other 'auto' variables defined within function bodies -- function scope.

Summary

So the 'static' keyword has different contexts with what amounts to "very different meanings." The reason it was used in two ways, like this, was to avoid using another keyword. (There was a long discussion about it.) It was felt that programmers could tolerate the use and the value of avoiding yet another keyword in the language was more important (than arguments otherwise.)

(All variables declared outside of functions have static lifetime and don't need the keyword 'static' to make that true. So this kind of freed up the keyword to be used there to mean something entirely different: 'visible only in a single compilation unit.' It's a hack, of sorts.)

Specific Note

static volatile unsigned char PORTB @ 0x06;

The word 'static' here should be interpreted to mean that the linker won't attempt to match up multiple occurrences of PORTB that may be found in more than one compilation unit (assuming your code has more than one.)

It uses a special (non-portable) syntax to specify the "location" (or the label's numeric value which is usually an address) of PORTB. So the linker is given the address and doesn't need to find one for it. If you had two compilation units using this line, they'd each wind up pointing to the same place, anyway. So there's no need to label it 'extern', here.

Had they used 'extern' it might pose a problem. The linker would then be able to see (and would attempt to match up) multiple references to PORTB found in multiple compilations. If all of them specify an address like this, and the addresses are NOT the same for some reason [mistake?], then what's it supposed to do? Complain? Or? (Technically, with 'extern' the rule of thumb would be that only ONE compilation unit would specify the value and the others should not.)

It's just easier to label it as 'static', avoiding making the linker worry about conflicts, and simply put the blame for any mistakes for mis-matched addresses upon whomever changed the address to something it shouldn't be.

Either way, the variable is treated as having a 'static lifetime.' (And 'volatile'.)

A declaration is not a definition, but all definitions are declarations

In C, a definition creates an object. It also declares it. But a declaration does not usually (see bullet note below) create an object.

The following are definitions and declarations:

static int a;
static int a = 7;
extern int b = 5;
extern int f() { return 10; }

The following are not definitions, but are only declarations:

extern int b;
extern int f();

Note that the declarations do not create an actual object. They only declare the details about it, which the compiler can then use to help generate correct code and to provide warning and error messages, as appropriate.

  • Above, I say "usually," advisedly. In some cases, a declaration can create an object and is therefore promoted to a definition by the linker (never by the compiler.) So even in this rare case, the C compiler still thinks the declaration is only a declaration. It is the linker phase that makes any necessary promotions of some declaration. Keep this carefully in mind.

    In the above examples, should it turn out there are only declarations for an "extern int b;" in all the linked compilation units, then the linker is charged with the responsibility to create a definition. Be aware that this is a link-time event. The compiler is completely unaware, during compilation. It can only be determined at link-time, if a declaration of this type most be promoted.

    The compiler is aware that "static int a;" cannot be promoted by the linker at link-time, so this actually is a definition at compile-time.