24

In C you can have external static variables that are viewable every where in the file, while internal static variables are only visible in the function but is persistent

For example:

#include <stdio.h>

void foo_bar( void )
{
        static counter = 0;
        printf("counter is %d\n", counter);
        counter++;
}
int main( void )
{
        foo_bar();
        foo_bar();
        foo_bar();
 return 0;
}

the output will be

counter is 0
counter is 1
counter is 2

My question is why would you use an internal static variable? If you don't want your static variable visible in the rest of the file shouldn't the function really be in its own file then?

hhafez
  • 38,949
  • 39
  • 113
  • 143

13 Answers13

39

This confusion usually comes about because the static keyword serves two purposes.

When used at file level, it controls the visibility of its object outside the compilation unit, not the duration of the object (visibility and duration are layman's terms I use during educational sessions, the ISO standard uses different terms which you may want to learn eventually, but I've found they confuse most beginning students).

Objects created at file level already have their duration decided by virtue of the fact that they're at file level. The static keyword then just makes them invisible to the linker.

When used inside functions, it controls duration, not visibility. Visibility is already decided since it's inside the function - it can't be seen outside the function. The static keyword in this case, causes the object to be created at the same time as file level objects.

Note that, technically, a function level static may not necessarily come into existence until the function is first called (and that may make sense for C++ with its constructors) but every C implementation I've ever used creates its function level statics at the same time as file level objects.

Also, whilst I'm using the word "object", I don't mean it in the sense of C++ objects (since this is a C question). It's just because static can apply to variables or functions at file level and I need an all-encompassing word to describe that.

Function level statics are still used quite a bit - they can cause trouble in multi-threaded programs if that's not catered for but, provided you know what you're doing (or you're not threading), they're the best way to preserve state across multiple function calls while still providing for encapsulation.

Even with threading, there are tricks you can do in the function (such as allocation of thread specific data within the function) to make it workable without exposing the function internals unnecessarily.

The only other choices I can think of are global variables and passing a "state variable" to the function each time.

In both these cases, you expose the inner workings of the function to its clients and make the function dependent on the good behavior of the client (always a risky assumption).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • This answer has been sitting around for a while I see, but it's not quite right. Indeed the `static` keyword does serve multiple purposes in C. However the important thing to differentiate is the _identifier_ used to reference an object, and the _storage_ used by the object. The `static` keyword *always* limits the visibility of the identifier to the current scope, be that at the file level or block level. The confusion comes because `static` also changes the _storage_ _duration_ of the object when it is used with a variable at _block_ scope. Note the diff. of _identifier_ and _storage_. – Greg A. Woods Dec 12 '12 at 06:58
  • One way to remember this difference is to remember that at _block_ level the keyword `auto` is the default for _block_ level identifiers _unless_ it is overridden by explicitly using the `static` keyword. I suspect that if the keyword `auto` were always required to be present unless `static` were present, then the level of confusion over the double meaning of `static` would be far reduced. – Greg A. Woods Dec 12 '12 at 07:01
  • @Greg, I'm not sure I agree with that totally though it's always possible I've misunderstood your intent. `static` has zero effect on the visibility of an identifier within a _block._ It's the fact that it's in a block that limits its visibility to that block and `static` doesn't actually change that. Making a block-level variable into a static changes _only_ its storage duration. – paxdiablo Dec 12 '12 at 07:27
  • Well strictly you are correct, but as I said, those strict rules are what causes confusion. If you think of `static` as _always_ restricting scope then you cover the case of a variable with internal linkage (file level scope) as well as a variable with linkage of "none". For block-level variables I think it is then easier to think of `static` replacing, or kicking out, the implicit `auto` and thus _also_ changing their storage duration. The strict rules are only necessary to understand in more detail if you're writing a C compiler, but not if you're just using the language. – Greg A. Woods Dec 12 '12 at 20:03
  • I.e. thinking of `static` as also restricting the scope of an identifier which has already got restricted scope is harmless in general, and doing so helps unify the conceptual application of `static` to identifiers with file scope (which refer to objects which already implicitly have static storage duration, and where the explicit use of `static` only converts them from having external to internal linkage). – Greg A. Woods Dec 12 '12 at 20:06
  • I maybe shouldn't have said "not quite right", but rather something more along the lines of "not so helpful in the context of the original question". – Greg A. Woods Dec 12 '12 at 20:08
9

They are used to implement tools like strtok, and they cause problems with reentrancy...

Think carefully before fooling around with this tool, but there are times when they are appropriate.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • Would you have any links for further reading about such problems? Thanks! – yungchin Feb 10 '09 at 23:33
  • Not really. I came to understand these beasties by osmosis. I think the implementtion of strtok is instructive, as is that of strtok_r. Generally the later patter is preferred... – dmckee --- ex-moderator kitten Feb 10 '09 at 23:35
  • The problem would be if you'd eg. if you used strtok to split at ":", and called another function which itself calls strtok on the tokens (maybe because it would need to split some token at " ") – jpalecek Feb 10 '09 at 23:42
  • Thanks, I've had a look at strtok [here][1] - without even seeing the code, just the usage specification tells me that it's an awful design! I'm not sure if that means all uses of local static variables are bad though... [1]: http://www.cplusplus.com/reference/clibrary/cstring/strtok.html – yungchin Feb 10 '09 at 23:51
  • With strtok in particular, the problem is that there is a static pointer to the string being worked on. It is safe to change the separaters if you want, but _not_ safe to interleave calls involving two different strings. Now try to guarantee that when running multiple threads... Locking nightmare. – dmckee --- ex-moderator kitten Feb 10 '09 at 23:52
  • The design of strtok isn't awful, as such. Just minimal and fragile. This is an _old_ tool. It does one, simple thing. – dmckee --- ex-moderator kitten Feb 10 '09 at 23:54
  • strtok() is not bad because it uses static but because it changes the string you pass to it. It's quite easy to implement a strtok() with thread-specific data that is thread safe. – paxdiablo Feb 11 '09 at 01:15
  • thread safe strtok() would still fail on code like ... for(token=strtok(line, ":"); token!=NULL; token=strtok(NULL, ":")) /* split line into tokens */ { for(pathcomp=strtok(token, "/"); pathcomp!=NULL; pathcomp=strtok(NULL, "/")) /* parse path in some of the tokens, possibly in different function */ – jpalecek Feb 11 '09 at 10:26
7

For example, in C++, it is used as one way to get singleton istances

SingletonObject& getInstance()
{
  static SingletonObject o;
  return o;
}

which is used to solve the initialization order problem (although it's not thread-safe).

Ad "shouldn't the function be in its own file"

Certainly not, that's nonsense. Much of the point of programming languages is to facilitate isolation and therefore reuse of code (local variables, procedures, structures etc. all do that) and this is just another way to do that.

BTW, as others pointed out, almost every argument against global variables applies to static variables too, because they are in fact globals. But there are many cases when it's ok to use globals, and people do.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • I won't vote you down but I see two problems - (1) The question was for C. (2) File level statics are globals only in duration, not in visibility, so they're at least a little better. – paxdiablo Feb 11 '09 at 01:14
  • (1) Yes, but I think in C++ they are more useful than in C, so it's better to demonstrate (2) a little better, but all the reentrancy and threading issues are common for singletons, static and global data – jpalecek Feb 11 '09 at 10:23
4

I find it handy for one-time, delayed, initialization:

int GetMagic()
{
   static int magicV= -1;

   if(-1 == magicV)
   {
      //do expensive, one-time initialization
      magicV = {something here}
   }
   return magicV;
}

As others have said, this isn't thread-safe during it's very first invocation, but sometimes you can get away with it :)

DougN
  • 4,407
  • 11
  • 56
  • 81
  • I quite agree. I've seen many, many multithreaded routines where I could prove the first call was threadsafe. – Joshua Feb 11 '09 at 00:35
  • 1
    And if you're compiling with something like -D_THREADSAFE, the code would have protection around the if statement, or it would be a requirement to call GetMagic in the main thread before starting other threads. In other words, there are way around the problem that don't break encapsulation. – paxdiablo Feb 11 '09 at 01:10
1

I think that people generally stay away from internal static variables. I know strtok() uses one, or something like it, and because of that is probably the most hated function in the C library.

Other languages like C# don't even support it. I think the idea used to be that it was there to provide some semblance of encapsulation (if you can call it that) before the time of OO languages.

Dave Markle
  • 95,573
  • 20
  • 147
  • 170
  • 1
    :-) Most hated? I think you'll find setjmp() and longjmp() may be fighting for that honor. Sort of goto on steroids. – paxdiablo Feb 11 '09 at 01:12
  • And gets() - unless you sipmly don't count it at all...whereupon setjmp() and strtok() get to fight over who's worse. – Jonathan Leffler Feb 11 '09 at 02:59
  • LOL, it was the most hated for my boss a couple of jobs ago. ;-) – Dave Markle Feb 11 '09 at 12:21
  • 2
    Local permanent storage in functions is not necessarily a bad idea. Local permanent storage in library functions is. – David Thornley Feb 12 '09 at 15:37
  • @DavidThornley You mean if the local permanent storage in library function is used by two different threads, it will create a problem ? I am just clarifying my understanding. – duslabo Oct 27 '13 at 17:38
1

Probably not terribly useful in C, but they are used in C++ to guarantee the initialisation of namespace scoped statics. In both C and C++ there are problemns with their use in multi-threaded applications.

1

I wouldn't want the existence of a static variable to force me to put the function into its own file. What if I have a number of similar functions, each with their own static counter, that I wanted to put into one file? There are enough decisions we have to make about where to put things, without needing one more constraint.

gbarry
  • 10,352
  • 6
  • 33
  • 43
1

Some use cases for static variables:

  • you can use it for counters and you won't pollute the global namespace.
  • you can protect variables using a function that gets the value as a pointer and returns the internal static. This whay you can control how the value is assigned. (use NULL when you just want to get the value)
George
  • 15,241
  • 22
  • 66
  • 83
1

I've never heard this specific construct termed "internal static variable." A fitting label, I suppose.

Like any construct, it has to be used knowledgeably and responsibly. You must know the ramifications of using the construct.

It keeps the variable declared at the most local scope without having to create a separate file for the function. It also prevents global variable declaration.

For example -

char *GetTempFileName()
{
  static int i;
  char *fileName = new char[1024];
  memset(fileName, 0x00, sizeof(char) * 1024);
  sprintf(fileName, "Temp%.05d.tmp\n", ++i);
  return fileName;
}

VB.NET supports the same construct.

Public Function GetTempFileName() As String
  Static i As Integer = 0
  i += 1
  Return String.Format("Temp{0}", i.ToString("00000"))
End Function

One ramification of this is that these functions are not reentrant nor thread safe.

user62572
  • 1,388
  • 2
  • 15
  • 24
1

Not anymore. I've seen or heard the results of function local static variables in multithreaded land, and it isn't pretty.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • There are ways and means to make this workable without sacrificing encapsulation. Look up "thread-specific data" or see http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=/rzahw/rzahwdatco.htm for examples. – paxdiablo Feb 11 '09 at 01:06
  • Oy, thread-local-storage isn't that much better. Extra untrackable compiler magic to support TLS isn't exactly my cup of tea. – MSN Feb 11 '09 at 05:50
  • Or if you make the function atomic (or whatever the word is) so it blocks other threads from using it when one thread is executing it. Just another example of how changeable state is a headache in threaded programming. – David Thornley Feb 12 '09 at 15:36
1

All statics are persistent and unprotected from simultaneous access, much like globals, and for that reason must be used with caution and prudence. However, there are certainly times when they come in handy, and they don't necessarily merit being in their own file.

I've used one in a fatal error logging function that gets patched to my target's error interrupt vectors, eg. div-by-zero. When this function gets called, interrupts are disabled, so threading is a non-issue. But re-entrancy could still happen if I caused a new error while in the process of logging the first error, like if the error string formatter broke. In that case, I'd have to take more drastic action.

void errorLog(...)
{
    static int reentrant = 0;
    if(reentrant)
    {
        // We somehow caused an error while logging a previous error.
        // Bail out immediately!
        hardwareReset();
    }

    // Leave ourselves a breadcrumb so we know we're already logging.
    reentrant = 1;

    // Format the error and put it in the log.
    ....

    // Error successfully logged, time to reset.
    hardwareReset();
}

This approach is checking against a very unlikely event, and it's only safe because interrupts are disabled. However, on an embedded target, the rule is "never hang." This approach guarantees (within reason) that the hardware eventually gets reset, one way or the other.

Casey Barker
  • 676
  • 1
  • 5
  • 10
1

In writing code for a microcontroller I would use a local static variable to hold the value of a sub-state for a particular function. For instance if I had an I2C handler that was called every time main() ran then it would have its own internal state held in a static local variable. Then every time it was called it would check what state it was in and process I/O accordingly (push bits onto output pins, pull up a line, etc).

Stephen Friederichs
  • 1,029
  • 6
  • 12
0

A simple use for this is that a function can know how many times it has been called.

lillq
  • 14,758
  • 20
  • 53
  • 58