Why Static Variables?

I am trying to create a list of “good” programming practices for the students. I cannot seem to google a good reason for proper use of static variables instead of globals. Anyone interested in sharing some knowledge?

When is a static variable required over a global?
What are the advantages of static variables?
What are the disadvantages of static variables?

Thanks!

There might be something that I’m forgetting, but I believe that the only advantage of static variables over globals is that a static variable is self-explanatory in the fact that it will only be used in that function. A global variable could be changed in other functions and someone reading your code would not know whether or not it is. If you are familiar with classes (in C++ for example), this is basically the same advantage as using private variables over public variables. I hope this was helpful, and good luck…

The keyword here is “scope”.

Scope for a global and a static in a local function is different.

A static is persistant, therefore the permanent portion of the memory map is larger, more memory usage (a disadvantage). But the static could be local to a function.

A global is essentially static, but the scope is precariously large.

If you use a global variable, it is easier to accidentally screw it up, by accidentally reusing the same name in a different function. Since static variables only stay in the function it was constructed in, the same name can be used in various places. For example, variable names like “count” and “loops” might be used more than one time in your code. If they were global, that would cause a problem, however a static would allow you to do this without problems.

Two properties of variables in C are persistence and scope or visibility. Globals and statics are persistent since they hold their values for the duration of program execution as opposed to automatic variables which become invalid when the execution of the program exits the scope where the automatic is defined. Variables allocated on the heap persist between the malloc (or similar) and free calls.

Global variables have program scope meaning they can be seen anywhere in the entire program provided a declaration is visible. Static variables can have file, function, or even block scope. Automatic variables can only have function and block scope by contrast. The visibility of heap variables is controlled by the visibility of the pointer variables which, well, point to them.

Good programming practices usually suggest that variables be given the smallest scope to do the job. This prevents accidental access via things like mispelling the variable name. It also limits name clashes for commonly used variable names and makes the logic easier to follow. That said, most programmers don’t use block scope in C and the smallest common scope is the function. Using more descriptive names helps with name clashes but it’s nice to be able to use a simple descriptive name without having to worry too much about it until the linker complains. Some coding styles preface global variables with “g_” and file scope statics with “s_” to help with clashes and to make it more obvious what the variable really is.

With microcontrollers like the PIC which have limited variable space and especially limited stack space, it can be more efficient to use statics or grobals to more efficiently use memory. Also, from a speed standpoint, some microcontrollers can access static memory faster than a stack variable (I’m not sure about the PIC in this respect). Memory management with a microcontroller can take more understanding than for your typical PC program.

Excellent guys, thanks for the info.

I actually wouldn’t worry about static variables too much. They are an optimization that is rather easy to apply later.

Always optimize for readability.

Always be prepared for the MacTruck Event. (explicitly, a person ((you)) gets removed from play, typically with out warning).

I had to take over programming in my sophemore year of high school when our programmer suddenly could not attend competition. A few premature optimizations left the drivetrain code completely unreadable. I eventually had to kill that part and start over.

Static variables are NO WHERE NEAR that level of confusing, but just keep in mind that other people will read the code. If an optimization took you a decent amount of time to figure out, it will take even longer for the reader to.

Therefore, use local variables where it is good to do so. If you want to make it faster later, just type static in front of their declarations. I would hope the compilor would do that for you, but this ain’t the brightest light in the box.

It seems to me like there are several conflicting explanations on the use of static variables.

There are two main types of static variables:

  1. Static variables declared inside functions
  2. And static variables declared outside functions

The first type means that the variable is persistent between calls of the function.

Let’s say I have the function below:


void function (void) {
    static int counter = 0;
    int fakeCounter;
    counter++;
    printf("Counter: %d
", counter);
    printf("Fake Counter: %d
", fakeCounter);
}

Here, the variable counter is initialized to 0 at compile time. When the function is first called it will print out “Counter: 1”, the second time it will print out “Counter: 2”, and so on. As you can see, the static variable keeps its value between calls. In this case, the keyword static refers to the static storage class. In the static storage class, the data is stored in normal RAM and the variable in the static storage class will always be guaranteed to stay the same unless you explicitly change their value. Therefore, it is unlike a normal variable inside a function, which is in the “auto” storage class. Variables in the auto storage class are stored in the “stack”, which is sort of like a piece of scratch paper used by the function on which to store information. Since this scratch paper gets “thrown away” when the function is completed, variables in the auto class are not persistent and can be used for internal work within a function. (One disadvantage to using this piece of scratch paper is that more instructions are necessary to access the variable) Therefore, you can NEVER replace a normal (i.e. auto) variable with a static variable.

----You can skip reading this if you wish, but it is interesting:) ----

However, I believe you can use the “overlay” storage class in order to save CPU time since the variable is stored in normal RAM rather than the function stack. However, the variable is reinitialized upon every call of the function and other functions with overlay variables are allowed to use the memory locations of other overlay variables that are in functions which do not call the current function. See example below:


void f1(void) {
    overlay int i;
    i = 0;
    f2();
}
void f2(void) {
    overlay int j;
    j= 2;
}
void f3(void) {
    overlay int k;
    k = 3;
}

In the above set of functions “k” can be stored in the same location as i or j, but i and j obviously cannot share the same location. Therefore, I believe you can use overlay variables in place of normal auto variables wherever you wish (unless you are using recursion). However, you may end using more memory though if you have a lot of nested functions.

----Start reading again----

Now after this long digression, the explanation of what the other “static” means.

When static is used with a global variable, it means that the global variable cannot be accessed from outside the file in which it is declared. That is, if i declare static int a = 0 in file1.c a function in file2.c cannot access the variable “a”. This version of the static modifier can also be used with functions in order to indicate that the function cannot be called from outside the file.

This isn’t true, static variables are not an optimization. They in fact make your code more readable. (However, if you replace static with overlay this is true.) However, I definitely do agree with the rest of EricVanWyk’s post.

I hope this insanely long and boring post was helpful:p

Wow, I just got pwnt. Very nice post.

Can you link us to more information on overlay variables? I am unfamiliar with them.

The optimization I was referring to was the distinction between using the stack and not using the stack. Usually when one of my students proposed using a static variable, this was the purpose.

I think the issue was the difference between:

static int i;
i=0;

and


static int i=0;

I believe the first usage is where “overlay” is useful?

Sorry for any confusion. I’ve actually fixed more C code than I’ve written myself, so I’m more familiar with bad strategies than good ones.

In general, I’d force them to explain exactly why they want to use these static variables.
A counter internal to a function? Great.
An ill-informed attempt at scope-narrowing? Bad.
A well informed attempt at scope-narrowing? Good.
Writing a function and assuming it will always operate on a single set of data? NIGHTMARE. What happens if you suddenly grow another wheel/senesor/anything?

I do love “as narrow as possible” scoping, but I prefer placing variables with their buddies.

This is a paraphrased version of why I demand buddy variables share scope:


int softenMotor(int intended_value){
static int old_value=127;
int new_value = (old_value+intended_value);
old_value=new_value;
return new_value;
}

Oh goodness, the left and right motors are suddenly bound together through old_value.

I know I am being overly cranky. I know that no body would actually do that (more than twice). I am simply speaking as one who has burned himself with premature optimization time and time again.

If variables are “buddies”, then group them in a struct. If the data needs to persist, then make the instance of the struct static. Functions that work on the data can then just take a pointer to the structure as an argument so one function can handle both left and right wheel data, for instance.

Write file modules around functions that work on one structure type. This allows you to mimic some of the good organization that comes with using C++ classes in C. File scope static variables are equivalent to class static variables. Static functions are equivalent to protected or private C++ functions. Only expose the public interface to struct with the global functions. This keeps things pretty encapsulated. You can preface all the global functions with a name related to the structure they work on to minimize name clashes.

Thank you.:slight_smile:
All the information I gave you was from the C18 Compiler manual, specifically page 20 of the User’s Guide PDF on Kevin’s site. (This is actually page 12 of the manual if you happen to have it printed out.)

One of the the key issues with overlay is that any sort of recursion is impossible. (e.g., you couldn’t use it in the typical factorial implementation).
You will be given an error by the linker if this is the case though, so you shouldn’t have to worry about random unknown errors popping up because of this.

The example you gave later in your post is correct (assuming I am inferring your intentions correctly). A static variable is used when you want the value of the variable to stay the same between function calls. An overlay or auto variable is used when you do not want that to be the case.

If that was the case, then they would not be optimizing the code, they would most likely be breaking it.

This is very good advice. Structs can help your code become much more organized. However, I personally prefer not using pointer arguments in robotics code, except for utility functions. If the structure needs to be used by more than one function make it a global or have it return a struct.

Very true. Keeping scope small also leads to much more readable code. For example, lets assume you are debugging a function that was written with no global variables. In this case you know that all function input MUST be passed through the arguments given to the function at the time the function was called. Once you verify the integrity of the paramaters passed the scope of your debugging is entirely within that function because it is impossible that any data besides the originally passed paramaters had made its way into that function.

On the other had if you have use of global variables then you have no conrtol over the data that the function is using. Vars could be changing mid-function making debugging work very hard.

Example: Your using variable x which is global. You call function process_x() passing it no args since x is global and you dont need to. At some point within function process_x you call another function process_y. Since its been a while since you wrote process_y you forgot that it tweaks the value of x. Because of this process_x starts screwing up and you spend WAY to long debugging it.

Out of curiosity, is the only difference between a global variable accessed by the function and a static variable created and accessed by the same function scope? It seems that since both the static variable and the global variable are stored in standard RAM, they would have the same speed, and static variables could simply not be accessed by other functions as globals could. (This isn’t to imply that statics are useless, as they would avoid many name conflicts, although I tend to name all of my variables exclusively anyway. I’m only trying to clarify that my grasp of their use is correct).

Another good practice is to indentify pointer parameters that won’t change with the keyword const. This allows the user of a function to know what is passed won’t be changed by the function. If const is absent from the function declaration, the user can assume the data does in fact change inside the function. It also helps the function author in that the compiler can help enforce the constness in case the author accidentally tries to modify the data or forgets during changes to the function. Of course, this only applies to data passed via pointers since simple arguments are stack copies and it’s only that copy the function changes.

This is a good example. Using globals this way is a good way to not be able to see and understand what happens to the data. It also leads to a coupling the code to specific data and keeps the code from being as generically reusable as possible. A better way even if the data is global is to pass it as a parameter. A pointer to struct in the case of structures.

Another example, although this one there’s not a lot that can be done about it and it is relative rare but does occur, is variables associated with interrupts. They need to be clearly identified with the volatile keyword so everyone knows the value can change at any instant and if they’re working directly on the variable, they need to protect it for the duration of use.

Yet another example which doesn’t apply to RC code is multithreaded code or shared variables in multiple processes. All of what applies to your example applies to these types of applications in spades. You don’t get a nice linear debug path to find where the data is changing.