Embed With Elliot: The Static Keyword You Don’t Fully Understand

One of our favorite nuances of the C programming language (and its descendants) is the static keyword. It’s a little bit tricky to get your head around at first, because it can have two (or three) subtly different applications in different situations, but it’s so useful that it’s worth taking the time to get to know.

And before you Arduino users out there click away, static variables solve a couple of common problems that occur in Arduino programming. Take this test to see if it matters to you: will the following Arduino snippet ever print out “Hello World”?

void loop()
{
	int count=0;
	count = count + 1;
	if (count > 10) {
		Serial.println("Hello World");
	}
}

If you said, “Yes” you absolutely need to read this article. If you said “No, you need to define count as a global variable outside of the loop(), silly!”, you got the answer right in principle, but defining the variable as static is yet better. And we’ll see why.

But the shortest possible summary of this article is the following: if you want a variable to retain its value between calls to the function that contains it, declare that variable static.

And the summary rationale is that declaring a variable to be static sets the scope to be local, but makes the variable stick around for the lifetime of the program, rather than just the function call in which it was defined. Which means that it’s not re-initialized on every call to the function, and that lets us effectively store data within a function. It’s a great trick to have in your programmers’ toolbag.

Scope and Duration

To understand static, we’ll have to tease apart two related concepts of a variable’s availability to our code: scope and duration (or lifetime).

Most of you will know about variable scope. In many languages, if you declare a variable within a function or block, it stays inside that function or block. That’s called “local scope” or “function scope” or similar. Other functions can’t use that variable without our function explicitly passing the variable out. This is great, because it means that you can name all your counter variables count and they won’t collide with each other across functions. You probably know this.

Variables in C & Co. also have a lifetime that they’re guaranteed to be available for, also called their “duration”. After their duration is up, you won’t be able to use the variable any more. This is closely related to the idea of “scope” which specifies in which functional blocks of code the variable is available, but you’re going to want to keep them separate in your mind. Easiest is to think of duration as being a time, and scope as being a location (in code-space).

The default duration of a variable is the current call of the function. That’s why something like:

int counter (void)
{
	int count=0;
	count=count+1;
	return count;
}

will always return 1. Each time you call the function, count gets re-initialized to zero, and when the function returns, that variable’s storage gets reallocated because it has function duration. (And this is exactly the problem with the Arduino snippet above.)

We want our counter() function to keep track of how many times it’s been called. We need the variable count to have program duration — it needs to stick around as long as our code is running, and not get reallocated after every function call.

Globals?

The brute-force method is to declare count as a global variable by defining it outside of any functions. Global variables live for the duration of the program, so the value contained in count will stay available. So far, so good. But variables with global scope have the side effect of making count available from any other function. Sometimes this is exactly what we want, but a lot of the time it’s not.

The problem with global scope is that you can’t use any other global variables of the same name, so you need pick the names carefully to be unique so that they don’t clash. This isn’t a huge problem if you just use long, specific names: My_Global_Loop_Counter instead of count, for instance.

A more subtle problem arises when you intend to use another variable named count in a second function, but forget to declare it. The second function thinks that you meant to use the globally defined count, and havoc ensues.

Finally, it’s tempting to use global variables to pass data among different functions. (This is especially true for people who haven’t yet made peace with pointers and structures.) If you spread the functions using a global variable across different files, it can take heroic feats of debugging to track down every location where a popular global variable is accessed or modified. That is to say, global variables can lead to fragmented, hard to debug code.

No, Static.

Anyway, if you buy the arguments above, what you really want is a variable with program duration but function scope. And that’s exactly what static does when it’s applied to a variable inside a function. The language has precisely the feature you want, so you might as well use it.

And to bring it on home, the Arduino snippet up above will work just fine with a globally defined count variable,

int count;
void loop()
{
	count = count + 1;
	if (count > 10) {
		Serial.println("Hello World");
	}
}

but it’s also a lot cleaner when written using static:

void loop()
{
	static int count;
	count = count + 1;
	if (count > 10) {
		Serial.println("Hello World");
	}
}

because the name “count” isn’t globally exposed. It’s also declared in the function that uses it, so there’s no question of to whom it belongs. And it works. You can’t beat that.

Static and global variables (all variables with program duration) are pre-initialized to zero by default, so we didn’t need to write static int count = 0; above. However, if we wanted the initial value to be non-zero, we could define the variable like so: static int count = 42;.

There’s something creepy about static int count = 42;, though. It looks like we’re declaring the variable and then setting its value on every pass through the code. But the static keyword prevents this. Trust us, or modify the demo code above to test it out for yourself.

Data Hiding and Singletons and Stuff

There’s a second, related, use of static to mention. When used outside of any functions, at the top-level of a file, static limits the scope to the file in question. Contrast this with global variables which have truly global scope and thus are available across files. Static variables defined outside of functions, then, are like a quasi-global variable: available for the duration of the program from every function defined within the file, but not reachable from outside the file.

Here’s an example where this quasi-global functionality is useful. Say we’d like to spin off the counter code into a counter library. We’re starting off with something like this:

int counter(void){
	static int count;
	count++;
	return count;
}

Every time you call counter() from other code, the function keeps the old value of count, adds one to it, and the counter behaves like it should. This is the function-scope static definition.

But now imagine that we’d also like to reset the counter. We might want a reset() function that will set the value back to zero. But count has function scope, so our reset() function won’t be able to touch it. The solution here is to use the static type declaration at file scope.

You could have something like this in your file “counter.c”:

static int count;

int counter(void){
	count++;
	return count;
}

void reset_count(void){
	count = 0;
}

static void my_secret_function(void){
	count = 42;
}

Because count is defined static at file scope, all the functions in the file will be able to use it, and the value will persist between function calls. Additionally, because my_secret_function() is defined static, it’s not callable from outside this file, though functions defined here can call it as usual.

Defining variables (and functions) static at file scope is perfect for storing library-specific stuff that is needed for the library, but that doesn’t need to be seen on the outside. This is a lot like what object-oriented languages do with public and private data and methods.

In contrast to the object-oriented pattern, though, we’ve only got one instance of our data. You can’t create a second one easily. This is what the software engineer types call a singleton.  These singletons are great for keeping track of global state, where you just write some simple functions to modify and access the data. Since the data is static and file-local, you know that you can’t mess it up from outside, and the manipulation functions are available anywhere you can #include them.

The drawback of singletons are that there’s only ever one of them. If you wanted two counters, for instance, this code wouldn’t help. When you get to such cases, you’ll want full-blown object-oriented support.

… and Arduino

This was a lot of heavy C theory, so you might think it’s not applicable if you’re programming in Arduino. If you think like that, you weren’t paying attention during the last “Embed”; Arduino is C/C++ with added convenience libraries. And in fact, the particular way that Arduino repeatedly calls the loop() function makes knowing a bit about scoping nearly mandatory: all of your function-call-duration variables get wiped each time through the loop unless you do something.

As we saw in the introductory example, you can’t initialize a variable inside the loop() without it resetting each time through the loop. If you didn’t know about the static keyword, you’d probably take the brute-force solution and define the variable as global. A bunch of the Arduino examples do just this.

What could go wrong?

int i;

void loop(){
	i++;
	Serial.println(i);
	delay(100);
	doSomethingTwice();
}

void doSomethingTwice(){
	for (i=0; i<2; i++){
		Serial.println("twice?");
	}
}

Well, say you’ve gotten in the habit, as we have, of using i as a generic loop variable. And say you re-used it without definition in another function, maybe even in another “.ino” file, that you call from within the loop. Because i is a global variable, the accidental second use is actually perfectly kosher. The compiler won’t be able to help you find the mistake, but your program won’t work right. Instead of counting up, the poor Arduino will just keep printing “3”.

There are two causes of this problem: forgetting to declare i inside doSomethingTwice(), and creating a global variable with an easy-to-reuse name. You will forget to declare variables from time to time. We all do. And when we do, we want the compiler to let us know.

So there are two ways to avoid the problem. The first solution is to name your global variables something crazy so that they’re unlikely to be reused. “My_Amazing_Global_Loop_Counter” would work well. Even typing it once makes me never want to type it again.

The “right” solution solves the initial problem that led us to use a global variable in the first place. We simply define our counter variable as static inside loop() and then it has program duration but function scope and all’s well. Now we can even call the counter variable i with impunity, because it’s scoped to loop().


void loop(){
	static int i;
	i++;
	Serial.println(i);
	delay(100);
	doSomethingTwice();
}

void doSomethingTwice(){
	// You'll get an error here b/c i isn't declared.  Thanks, compiler!
	for (i=0; i<2; i++){
		Serial.println("twice?");
	}
}

And what do you do if you want to share variables among different functions within a single “sketch”? One way is to pass them directly as arguments, but again you’ll see lots of folks resort to global variables that can be simply accessed from each relevant function. It’s not a huge problem if you take care to name the variables well, so we won’t insist.

But you could also do it “correctly” and define the global variables as static at the file level instead. You’ll still be able to make subtle mistakes within your own functions, but by declaring the variables static you won’t run the chance of confusing them up with variables defined elsewhere in the Arduino infrastructure. It’s a belt when you’re already wearing suspenders, but it doesn’t cost you anything except a tiny bit more typing.

Conclusion

Use the static keyword for variables within a function when you want the value to persist across function calls, but you don’t need to enlarge the scope. Use the static keyword for functions and variables at the file level when you want them to behave like quasi-globals, being accessible everywhere within the file but hidden from the outside.

See? That’s not so mysterious after all.

70 thoughts on “Embed With Elliot: The Static Keyword You Don’t Fully Understand

    1. It’s not different.

      Static changes the linkage (storage class) of the object it’s attached to. An “auto” (or “register”) variable means “translation-unit (file) visibility, and local (function) storage.” Declaring a variable static inside a function means “this has translation-unit (file) visibility, but global (program) storage”.It tells the linker “you need to create a symbol for me, because I’m taking up real space.”

      Static member variables in C++ do the same thing. They change the storage from being attached to the class object (the ‘struct’ that the class “is”) to being global. So again, it tells the linker to create a symbol for it.

      Static member functions in C++ also do conceptually the same thing – they change the function from being attached to the class object to having global storage. If the compiler was stupid, and actually created code for every single object created (rather than reusing the code and using a this pointer), then it’d be obvious that ‘static’ for functions does exactly the same thing.

      Even in our non-crazy world, you can see the difference pretty easily: you can take the address of static member functions and pass those around, but you can’t take the address of non-static member functions and pass those around: so it’s clear that static member functions “live on their own” (just like static variables), and not attached to anything, whereas non-static member functions don’t.

      1. While not the “absolute” address of a member function, there is a “pointer to method” concept in C++. They’re actually quite useful; please look into them.

        1. Actually,that illustrates the difference between static/non-static methods really well.

          In order to call a pointer to a method, it needs to be called “through” an object (e.g. from http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible , the ” (x->*my_memfunc_ptr)(6, “Another Arbitrary Parameter”);” call): that is, the function doesn’t exist without the “this” pointer.

          A static member function exists on its own – it has global storage – and so you can takes its address and pass it around happily. It’s really the same concept.

          1. Pointers to methods need an object because all non-static member functions can access the object’s variables, those prefixed with this-> (even if the this-> notation can be omitted). So you really can’t call a non-static member function without an object.

            However, despite this being an introductory-level article targeted at arduino people, I’m surprised no one has mentioned that the use of static variables easily leads to non reentrant and thread-unsafe code. Not a concern with arduino, but relevant on PCs and also with microcontrollers if you use an RTOS.

      2. Yes. Except. That you are wrong on every single thing you say.

        Static variables inside functions don’t generate symbols. Only an area in the bss section.

        Static class member variables do not allocate storage towards the linker, as you still have to define them inside an actual code unit. Which is where the storage is actually allocated.

        Static member functions are conceptually completely different from static functions, one stays inside the code unit, one exports a symbol and can be linked outside the code unit just fine.

        Finally, you can take function pointers to member functions just fine. And with C++11 std::function stuff has become easier on this aspect.

        static function in C: do not export symbol. Make sure nobody else accesses me.
        static variable in C: store me in bss section, do not export a symbol. Make sure nobody else accesses me, but do allocate global storage.
        static function in C++: do not attach me to an instance, but DO export a symbol. Actual definition needs to be in a code unit, which allocates the actual code space. Can be accessed from any other code unit.
        static member variable in C++: do not attach me to an instance, but DO export a symbol. Actual definition needs to be in a code unit, which allocates the actual storage space. Depending on public/private/protected&friends can be accessed from other units.

        So to summary:
        static in C, do NOT export symbol = no access from other code units.
        static in C++, DO export symbol = access from other code units.

        Very different.

        1. Any storage reference does export a linkage in the object file format. It may not be explicitly named (thus may not be referenced – what you call a symbol), but it is tracked in the same way as a named symbol. It has to be for the linker to aggregate section storage requirements from a collection of object files and fix-up references that may be absolute. There are a lot of things wrong with your post. However the most egregious error – the fact you don’t know what BSS is or how it differs from rodata and rwdata when I do ‘static int joe = 5;’ – means I shouldn’t even argue with you anymore.

          1. Ha. You said BSS. It is rather painful that this is all still under the hood. Or that people think C is what they send to the pre-processor. Whereas I suppose in the case of C++, it is what they send to the pre-processor.

        2. “Static variables inside functions don’t generate symbols. Only an area in the bss section.”

          Sorry, this might not have been clear. It doesn’t create a symbol that the C linker will search through, but it still creates a local symbol, rather than just storing it in a register, because “static” makes its storage global. If I declare “i” as static, and dump the symbol table, I get something like “00000200 b i$1”. If I don’t declare “i” as static, there’s no symbol for it at all. It’s not exported as a linkable symbol, the linker ignores it, and it’s just grouped with the others in the final assembly (so multiple symbols can have the same name). The “i$1” is because it was inside a function, so it needs a unique name.

          “Finally, you can take function pointers to member functions just fine. And with C++11 std::function stuff has become easier on this aspect.”

          You can’t take the address of a non-static class member function and treat it just like any function pointer. That’s the point. It doesn’t exist if no class of that type exists anywhere. It’s the same thing as saying “i” doesn’t exist outside of that function. You have to call it through the object – in other words, it has the same lifetime as the object.

          “static in C, do NOT export symbol = no access from other code units.
          static in C++, DO export symbol = access from other code units.”

          That’s true, but it’s more related to C++ changing namespacing in general – switching away from code unit separation to namespace separation. In C, if you’ve got 2 static objects, the linker skips them because they’re local, and they both get assembled in as separate objects. In C++, if you’ve got 2 static objects in different namespaces with identical names, they still get assembled as separate objects because their linker names are mangled with the namespaces. The effective behavior is still the same: you create 2 objects with the same name, which don’t get linked together.

          “Very different.”

          It’s different from the linker’s perspective, yes, but conceptually it’s not. A static variable inside a function can only be accessed inside that function, but has global storage/lifetime. A static variable inside a namespace/class can only be accessed inside that namespace, but has global storage/lifetime.

          The linker difference is because C++ allows a way to access the internal namespace of other code units, and it uses name mangling and the C linker as a (horrible) hack to support that.

  1. Incidentally, in many cases in C++ (presumably including in Arduino) you can get a version of effectively a function-scope data structure (with much the same interface) that you don’t have to copy if you need two of (and doesn’t necessarily use any more resources for only one instance) by using an object:

    class looper {
    int count;
    public:
    loop() : count(0) {}
    void operator()() {
    count=count+1;
    if(count > 10) {
    Serial.println(“Hello World”);
    }
    }
    };

    looper loop;

  2. C is such a nice language. pity the c++ guys came along and ruined everything. (yes, I’m mostly serious).

    thanks for the tutorial. good info on a topic that does need explaining to non-expert C folks.

  3. Holy crap Elliot, reading this quality of article almost makes me believe government intervention is needed to teach programming.

    First off, Arduino is written in C++, not C.

    Says ” global variables can lead to fragmented, hard to debug code.” Then proceeds to use the GLOBAL Serial object in his examples.

    Somehow you manage to ramble on about the “static” keyword without ever mentioning linking, or why you would put it as part of an “inline” function defined in a header file.

    If you follow this guide, you will definitely NOT fully understand “static”.

    1. >Arduino is written in C++, not C.
      Are there diferences between C and C++ in behavior of static variables in functions? It seems to be part of “C++ is C with objects” pack.

      >proceeds to use the GLOBAL Serial object
      ATmega328 has only one hardware UART and one global Serial initialized on startup is typical practice on Arduino. Globals can be harmful but they don’t have to.

      1. C++ for microcontrollers in fact really should only use global objects for peripherals: and in fact, they should be completely static globals, most likely with a CRTP-type implementation like the PDQ_GFX library does. Serial still ends up with “this” pointers, which is just ludicrous considering it’s not like you can do “ser2 = new Serial()” and have it mean *anything*.

  4. Thanks for the great write-up about the static keyword! This definitely does explain it quite plainly.

    However, could you explain why Static seems to be reused in a different when changing between in function and out of function? For example, declaring a variable Static inside the loop() function in the fourth code example affects the duration, but not the scope. However, the use of Static outside a function, like in your section “Data Hiding…”, seems to affect the scope, and not the duration. Is there a historical reason why this keyword was reused instead of two different keywords being chosen for what seems like two different behaviors?

    1. Actually, what you’re seeing is the *default* storage class changing when you’re inside a function and outside a function. It’s not static that’s acting different. Static always means “global storage, local scope”.

      Variables inside a function normally have “auto” storage, which means local storage and the visibility of the function. So static changes the local storage -> global storage.

      Variables outside a function normally have “extern” storage, which means global storage and visibility. So static changes the global visibility -> translation-unit visibility (since you’re in the scope of the file).

      More technically, “static” means you reserve space for it in the data section, and you don’t create a global linker symbol for it. If you’re inside a function, you wouldn’t do that anyway, so not creating the link name doesn’t do anything – you just reserve space, which you wouldn’t normally do. If you’re outside a function, you already reserve space for it in the data section, so adding static doesn’t change anything – but it does prevent a link name from being created.

  5. @Elliot,
    This column maybe in reaction to my comment in your previous one about Arduino sketch beeing ‘C/C++’. As ‘C’ programmer for decades I perfectly understand the usage of ‘static’. Why I didn’t used it in the only Arduino sketch I ever written? Because of prejudges. I was not programming in ‘C’ but in Arduino sketch.

    That said, I don’t change my mind. Sketch are not C/C++ proper because they must be pre-processed before beeing compiled by gcc. The fact that this pre-processing is quite simple and can be done by hand don’t change anything to it!

    C require functions prototypes for forward reference. No prototypes required in Arduino sketches, etc.

    1. It _was_ aimed at your comment, but not because you’re the only person having the problem — it looks like a lot of the Arduino example code uses globals when they could use static. (Not like they have to use static, either. Just that it’s arguably better style to do so.)

      So thanks for the post topic idea. :)

      We seem to agree that .ino files are pre-processed in a trivial way to be turned into .cpp files and compiled normally. We disagree whether this triviality turns “Arduino” into a whole different language. I’m ok with that.

  6. While the article explains the term and use of the “static” constructor very well, actually using “static” is sloppy programming. And it’s scope is within the object it’s called in – and all the code within a given file is in an object, as is all the code within a given function(also an object, but not the parent) is scoped within the function(object). ALWAYS pass your variables and/or objects from one function to another and return them appropriately. Your memory will love you for it. Make a “carrier” object for your variables that need to be passed and instance it when needed. That way there’s not a bunch of variables floating around where they shouldn’t be. It’s like the Offspring said: “You gotta keep ’em separated…”

    What I would give for a native C# IDE for the Arduino. The one they provide SUCKS. No context heuristics… I guess I’m spoiled by VS…

    1. There is VisualMicro, which at least lets you use the C++ IDE in Visual Studio. It’s a little hokey since it still relies on the arduino command line compiler to work, and the plugin is a bit naggy about upgrading to the pro version. But, worth a shot if you haven’t seen it.

      Also, using Static isn’t sloppy if you do it correctly (Speaking from mostly C#/VB.net experience). it makes sense when there should only be one possible instance of that object withing the scope/lifetime of the application. I use it a lot for storing values loaded from a database that you need for the lifetime of the application and you know will never change (caching). You can use the Singleton pattern to prevent having variables floating around.

  7. Thanks for the article, I always enjoy reading about the nuances of particular languages. Perhaps you’ll do a similar one on the ‘volatile’ keyword (if you haven’t already)?

      1. No way. If you’re talking about a compiler or architecture that provides you a way to get the kind of barriers you need, you’re right. But not all compilers or architectures provide that. The barriers needed to avoid volatile aren’t standard C, so they don’t have to provide them. And some compilers are just stupid, so you end up having to use it.

        Personally I lean towards “volatile is easy; don’t use it unless you know what change it causes in the assembly.”

      2. This is not true in a microcontroller world. You need to use volatile if accessing a variable outside normal program flow, e.g. from an interrupt. Example when main loop is waiting for an interrupt to set a flag that data transfer is complete or something similar.

        Of course there are other (more safe) ways to do the same thing, but often volatile is the easy & simple way without too much overhead.

        1. Antti’s point about interrupts is spot on. Plus, there’s some other nuances…

          Short version: if a variable doesn’t look like it’s changing, an optimizing compiler will just replace it with a constant. Volatile tells the compiler not to do that.

          Volatile is next in the “keyword” series. :)

  8. I have never carefully checked the execution performance difference on using a passed variable versus calling an in function static one. It would be interesting to see the trade in total run time and run time variance if anyone has a few minutes to code up some long loops. I will do it myself, but first, I have to wait until I get back to my workbench.

    1. volatile is used when a variable can be modified outside of program flow, like inside an interrupt or another thread. This inform the compiler that it must take special care about it. Something like: Don’t optimize it out because it is never assigned in the normal flow.

      1. Yes, someone please make volatile more known. I spend many hours debugging code like this:

        static int flag=0;

        void interrupt_handler()
        {

        flag=1;

        }

        void my_func()
        {
        flag=0;
        enable_interrupt();
        while(flag==0)
        ;
        //process data from interrupt.
        }

        What happens here that compile sees that my_func assigns flag=0 and then runs while loop. So flag is always 0. So this can be optimized as:
        flag=0;
        enable_interrupt();
        while(1);

        It was also hard to figure this one out as if I put some debug stuff inside while loop, it sometimes starts working correctly. Not sure why that happened but probably because after external function calls compiler cannot figure out if something is touching flag or not and does not do that optimization.

        So fix for above is static volatile int flag=0; And after that everything works as expected.

  9. Excellent article. I would add one more small point, but it does border on the topic of style. I use global variables in embedded code for one thing only: communication between ISRs and foreground code. Except for the most trivial cases, Interrupts take care of tasks for foreground code to alleviate the foreground code from the messiness of polling.

    That way, whenever I see a global variable in my code, I know that it must be tied to an interrupt (and of course, I make sure to use the volatile qualifier for those!)

    1. I do almost exactly the same thing, but still I’ll use the odd global now and then when it’s convenient and doesn’t do any harm.

      Honestly, I didn’t mean to come down so hard on the globals. The namespace argument is borderline trivial if you adopt a sane naming convention. That globals _can_ confuse the code logic is true, but it’s a lot less important when you’re writing 4KB of code instead of 10,000 lines of code. Like the “evil” goto, I totally think that globals have their place.

      If you have system state that needs to be shared across a bunch of files’ worth of functions, you’re either going to implement some state structure and pass it all around (yuck) or use a global. I’m not sure that one or the other is clearer.

      Many low-level peripherals are also intrinsically global. EEPROM? Always there, global scope. Input pin registers? Ditto. Interrupts, as you mention.

      It’s just that where static variables fit, they fit great. And I’ve seen enough code (even my own) with variables declared global just to work around the duration/lifetime problem, where they could easily have been declared static and the scope kept local. The loop() thing in Arduino was the last straw, so I took off writing. :)

      1. I do use globals here and there, but my cringing at the thought (except in the case of foreground/background code communication) at least keeps that to a minimum. I do tend to opt for the pass a struct around approach, but I always (except perhaps in trivial cases) pass a pointer to the struct, even when the struct is globally declared. For example, I have some interrupt driven serial code that I wrote recently for a project. The code receives and transmits data packets using ISRs, and flags are set to notify foreground code that a transmission has completed or that a full (and valid) packet has been received. As a result, the structs that hold the packets are globally declared, but because my initialization code accepts a pointer to a struct. It doesn’t need to know anything about the specific instance of the packet that it is initializing.

  10. It may be a mystery, but I think nuances is a wrong word for this. You either understand the use of static or not.

    The way GCC AVR implements how program memory on the other hand is plain stupid. When it is anal about silly standards and gets in the way of your coding, then that’s when I would use the word nuance. It is down right a pain to store array of character strings in code space. You want to do that because the compiler would simply create the storage in your precious RAM even when it is a const. Most of the embedded compilers would handle it with either extended keywords and handles the access transparently without silly functions.

    1. Handling mixed pointer access to flash/RAM transparently would degrade performance and increase code size. The real fault lies with the AVR hardware designers who should have implemented transparent data reads from flash (ARM cores have had that feature)

    2. The extension types defined tells the compiler that the pointers are accessing the FLASH memory *explicitly*, so there is no penalties as you have stated. Most of the embedded compilers does it this way. GCC/AVR is ass backwards and only cause issues. It is not like their code is more portable anyways as you are already tied to the architecture in more ways than you think in embedded code.

      Now if you want to define a generic point that handle them all, it is easy too. In this case you are paying a penalty for the transparent usage. You are trading off performance in exchange for convenience and more than likely saving code space by noting having to have extra code alternate access – the compiler reuses their generic code.

      It is not like you don’t use abstractions at all in your code which also have penalties, so why complain about this anyway?

  11. What would be the advised implementation of data readiness flags, e.g. if we have interrupts where we prepare data (e.g. UART receive) and then we process data in the main loop, so we want to set a flag when data is ready for process? Would static have any use in this scenario?

    1. In C, you want to declare them as volatile global variables as the compiler might be too smart to “optimize” your code for you without realizing that the interrupt code might alter its values. The optional static attribute would make the variable invisible outside of the file which can help in information hiding. That’s the term I was taught in old school pre-ANSI C era.

      1. Interesting question. I’ve always just used plain-vanilla (volatile) globals for ISRs.

        Tekkieneet is probably right about the file-local stuff with “static”. I’ll have to think it through a bit to figure out if there’s a real benefit there.

  12. To continue the theme, you should also declare any functions “static” that are not meant to be referenced outside the module. Back in the CP/M and PDP-11 days this was more important, as it made the “linkers” job easier and took less memory. Today, not many people even care about static functions because they have virtual memory, and sloppy code works anyway.

  13. Good article but one thing…

    Static and global variables (all variables with program duration) are pre-initialized to zero by default, so we didn’t need to write static int count = 0; above.

    This is not always the case, it’s still good coding practice to have the extra 3 characters.

  14. Crikey, that’s such ‘old magic’ I’d long forgotten it (courtesy of a day job coding in c#, javascript and python).
    I’m loving these beginner/refresher articles BTW – thank you.

  15. Another important use of static within a function is to reduce the impact of a particular function on the stack. Local variables are stored on the stack, so if you happen to have a large array, or structure declared locally in a function, you can potentially overflow the stack. This is much easier to do on smaller MCUs with limited RAM (like the ATmega328). Some pretty weird things start happening when you overflow the stack, so being aware of the fact that local variables use the stack and local static variables do not is some valuable information indeed.

    1. So thats pretty much all static is in this case, all the static keyword does is change it from an auto var allocation, to a static allocation, the rest of the stuff being talked about it for an auto local var is an effect of that.

      it’s a very important aspect of it as you say, since it can change the code gen/data use immensely.

      kudos on being the only one to say it

  16. Arduino users using multiple .ino files beware!!!
    The Arduino environment merges all of the .ino files in a project into one .cpp file before compiling the project.
    If multiple .ino files exist in a project and a static variable is declared in one of the .ino files, it *will* be globally visible in all the .ino files in the project!!!

  17. HaD should do a post(s) on data structures and I don’t just mean struct’s. I’ve met a lot of college-trained-and-graduated programmers that don’t even know what a linked list looks like. Half of the time if you design a good data structure, the programming is pretty easy.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.