Tutorial on `static` and `extern` keywords in C and how they relate to global names This came up in a discussion post in our staff Slack, and students often wonder about it. I thought it might be worth a general overview for everyone. static At the top level (i. e., on global names, whether functions or global variables), `static` means that the name is visible only to the current compilation unit (a single .c source file and the .o file generated from it). What this means is the .o file does not have a symbol defined that other compilation units can find during linking. ==> static global variables and functions cannot, <== ==> by definition, be shared across multiple .c files. <== ==> The linker won’t be able to find them. <== This, in turn, means that multiple compilation units can define the same global name without conflict when linking. It _also_ means that if multiple compilation units define a static thing with the same name, then each one is separate from the others: it is not shared among all the files. We use `static` on something we define in a .h file when we want each file that includes the .h file to have its own, separate copy of whatever it is. We might do this on function definitions to allow the compiler to inline the functions. We might use it on a global variable (boo, hiss!), because each .c file, for some reason, needs to have a global variable of that name for itself. When used on a *local variable*, that is, inside a function (not top-level/global), `static` means something else: it means the variable is really a global variable, but visible only to the relevant function. It is initialized according to any initialization in the function, and then the value persists across function calls. These were called "own variables" in ALGOL (if you want some programming languages history), presumably, because the function owned the variable. They were used for stateful functions, but they are rarely used any more, because they wreak havoc in multithreaded code. (The C function `strtok` is in this category, and modern code should use `strtok_r` if thread safety could conceivably be an issue --- and I would almost always assume it could be an issue!) extern The `extern` keyword means that the thing being named is being _declared_ here, but is _defined_ elsewhere. Hmm... In C, a _definition_ both creates a name and informs the compiler about its type and also allocates space for the thing being defined. Something that is just a declaration creates a name and informs the compiler about its type, but does not allocate space. Examples: void bake(oven_t oven, int temperature, bakable_t loaf, unsigned int time) { Oven_on(temperature); Oven_insert(loaf); Timer_wait(time); Oven_extract(loaf); Oven_off(); } _defines_ a function named bake and thus instructs the compiler to set aside space and fill that space with the associated instructions. The code above is a definition. The following is not a definition: void bake(oven_t oven, int temperature, bakable_t loaf, unsigned int time); This line _declares_ but does not define a function named bake. It introduces the name bake and says it’s a void function with parameters of the given types, but there is no body of the function. If you had this line alone in a .c file and called the function, the compiler could check the types of the call and produce code to make the call, but the linker would have to find the definition somewhere. You could state that the function is externally defined explicitly (“here is a declaration, the definition is somewhere else”) thus: extern void bake(oven_t oven, int temperature, bakable_t loaf, unsigned int time); People get confused with this on functions, because, honestly, it doesn’t matter. The compiler can distinguish whether a function is being defined or merely declared by noticing whether the header is followed by a `{` or a `;`, respectively. On global variables, the `extern` keyword makes a big difference! extern size_t NUM_EXCHANGE_RATES; extern float exchange_rates[]; says that NUM_EXCHANGE_RATES and exchange_rates are a size_t and an array of floats, respectively, but *they are NOT defined here.* Their definitions (that set aside the space for them) are defined *EXTERNALLY*, in some other place. The above lines do not create space for the size or the array, they only allow code to refer to them in the hopes that the linker (or code further down in the file) will provide a proper definition. The way to share global variables across compilation units, therefore, is to have one .c file own the actual definition (no extern keyword) and have all the other .c files (perhaps by including a common .h file) declare the global variables as externally defined.