Extern And Global Variables
Welcome back and happy new year! Last time we looked at
global variables
and their initialization. Today we'll look at how to share global
variables between .c
and .m
code files.
Frequently, you define a global variable in one code file and use it in
another. For instance, the file MyView.m
might contain
the global variable doubleTapEnabled
:
// MyView.m
#include <Foundation/Foundation.h>
// ...
BOOL doubleTapEnabled = YES;
// ...
To use this variable in main.m
you might do this:
// main.m
#include <Foundation/Foundation.h>
BOOL doubleTapEnabled;
int main(void) {
// ...
NSLog(@"doubleTapEnabled = %i", doubleTapEnabled);
// ...
}
This will work the way you expect, but there's a subtle potential
gotcha here. Notice that in MyView.m
we initialize
doubleTapEnabled
to YES
. When the compiler
sees that, it interprets that statement as a global variable
definition and allocates space for the
doubleTapEnabled
variable and sets its initial value.
However, in main.m
we don't give
doubleTapEnabled
an initial value, which makes that
statement ambiguous: it could be a global variable declaration
for doubleTapEnabled
or a definition, with
doubleTapEnabled
initialized to zero (NO
).
The difference between a declaration and a definition
can be confusing since they're closely related (and the two words are
unfortunately very similar). A declaration tells the compiler
that a global variable, struct, class or function exists somewhere. A
definition gives the compiler all the information it needs to
generate the code for a global variable, struct, class or function.
A statement like
BOOL doubleTapEnabled;
can be either a declaration or a definition. At link
time, if an unambiguous definition isn't found, the global
variable will be created and initialized to zero; if an unambiguous
definition is found, that definition will be used to create
the global variable.
So if you do something like this:
// ERROR: won't link
// MyView.m
// ...
BOOL doubleTapEnabled = YES; // unambiguous definition
// ...
// main.m
// ...
BOOL doubleTapEnabled = YES; // unambiguous definition
// ...
You will get a linker error like "duplicate symbol _doubleTapEnabled"
because you told the compiler to create the same global variable in two
different places.
This is where the extern
keyword comes in. You use
extern
with global variables to create unambiguous
declarations. In addition to helping the compiler, it also clues in
anyone reading the code that the global variable is defined (and
possibly initialized) elsewhere, but you're simply using it here. So
we can rewrite our example like this:
// MyView.m
// ...
BOOL doubleTapEnabled = YES; // unambiguous definition
// ...
// main.m
// ...
extern BOOL doubleTapEnabled; // defined in MyView.m
// ...
And now doubleTapEnabled
is happily unambiguous everywhere.
It's common style to add extern
to all global variable
declarations in your header (.h
) files and provide a
corresponding definition in a source (.m
or
.c
) file. So the header file MyView.h
would
look like:
// MyView.h
// ...
extern BOOL doubleTapEnabled;
// ...
The source file MyView.m
is the same:
// MyView.m
// ...
BOOL doubleTapEnabled = YES; // unambiguous definition
// ...
And main.m
now looks like:
// main.m
// ...
#include "MyView.h"
// ...
Next time, we'll look at using
the
static
keyword to make to make a global variable
"private" to a source file.