One of the unfortunate side effects of C's insistence on a small number of keywords is keyword overloading where a single keyword has differing semantics depending on where it sits. You can read the standard if you want the precise definitions, but the purpose of this post is to provide a comprehensible guide to new and old C programmers.
Compilation Units
Before we begin, we need to explain some unforunate jargon: compilation module/unit. This refers to the source file, any #includes, but not anything that the preprocessor removes. Given:
The compilation unit contains:
- the contents of foo.h
- main function
Generally, you can use the terms source file and compilation unit interchangeably but for precision I will used the term compliation unit. If you're ever confused about what the compilation unit is, use the -E compiler flag.
1. Global variables
For variables defined outside of a function (file-scope) this acts like a global lowercased variable in Go. It means that this variable is not accessible outside of the compilation unit it appears in.
Always use unless you really intend to share this variable with other source files. It's less necessary for const globals.
2. Functions
This has the same effect as for global variables in that it hides this function from other compilation unit. It's similar to a lowercase function in Go.
Use for any function you know shouldn't be used outside of the file it appears in e.g. helper functions. The nice thing about doing this is that you can safely change these functions and only need to check usage in the file they're defined in, your users shouldn't be using your statically defined functions!
3. Non-Automatic variables
Full working example: https://godbolt.org/z/6hbxc9srn
This is probably the most 'magical' use of static. Generally, variables declared in functions are automatic (on the stack), they get removed after their scope ends. This form of static lets that data persist. The function Count() above, will return a counter for the number of times it's been called. This may seem magical, but actually it is quite similar to the first example, but instead of this being a global variable to the compilation unit it is global to this function.
Generally avoid unless this really is the nicest way available to persist some data between function calls.