Previous | Table of Contents | Next |
C-through Macros
The best way to declare share, export, and import variables is to define macros for the required C syntax. Figure 5.2 shows three suggested macro definitions and how to use them (along with rules for declaring local variables.) For variables you want visible throughout the file, use the SHARE macro before the file's first function definition to declare (and define) each variable. For example,
SHARE int x;
This statement expands to
static int x;
which gives x the desired visibility. Note that you never use SHARE inside a function because it would just define a local variable.
#define | SHARE | static |
#define | EXPORT | |
#define | IMPORT | extern |
Declaring Variables
Visibility | Storage | Where referenced | Storage/visibility specifier(s) |
---|---|---|---|
Local | Automatic | In block | Define at beginning of block with no specifier |
Local | Static | In block | Define as static at beginning of block |
Nonlocal | | In nested block | (No declaration, use implicit extern) |
File | Static | In function | Define as SHARE before first function in file |
Declare as IMPORT at beginning of each function where referenced | |||
Program | Static | In function | Define as EXPORT before first function in file where variable is to be allocated |
Declare as IMPORT before first function in file(s) where variable is used | |||
Declare as IMPORT at beginning of each function where referenced |
Callable from | Visibility specifier |
---|---|
Same file (only) | SHARE |
Any file | EXPORT |
The SHARE macro isn't a silver bullet to slay all of C's visibility monsters, nor does it boost C's visibility features into a class with Modula 2's. But it makes a lot more sense to write SHARE rather than static when you're trying to specify a variable's visibility.
In functions or blocks where you want to use a SHARE variable, declare the variable with the IMPORT macro:
void func1( void ) { IMPORT x; ... }
This declaration expands to
extern int x;
which, although unnecessary given C's default scoping rules, makes explicit the function's intention to use x. Unfortunately, C won't protect you against unintentional references to x in functions that don't declare x. Reducing the number of nonlocal variables is the best way to reduce accidental nonlocal references.
You also should specify SHARE for functions you intend to be callable only from within the same file. Use EXPORT for functions you intend to be callable from anywhere. Functions without a visibility specifier default to EXPORT, but I find explicit visibility reminds me which functions are callable from anywhere. (I often notice EXPORT functions that don't need such broad visibility and that are better defined as SHARE.) In addition, by grouping all SHARE functions after all EXPORT functions, you can organize your functions better.
For export variables you want visible throughout all source files (i.e., the entire program), use the EXPORT macro to declare (and define) the variable before the first function definition in the file "owning" the variable. In most cases, this is the file containing the main function.
The following example shows how to define an export variable:
EXPORT int x = 0;
This statement expands to
int x = 0;
which gives x the desired visibility and allocates storage for x. It's good programming practice to initialize variables in their definitions, especially variables for which C's default zero initialization is not within the variable's domain (valid values). I'd also recommend you place all EXPORT variable declarations before any IMPORT declarations. Note that, as with SHARE, you never use EXPORT inside a function.
In functions where you want to use a nonlocal variable, declare the variable with IMPORT at the beginning of the function:
void func1( void ) { IMPORT x; ... }
The IMPORT declaration adds the necessary extern specifier and clearly shows that the function uses a nonlocal variable. A variable declared as IMPORT in a function will resolve to one of the file's SHARE or IMPORT external declarations.
In files where you want to use a variable exported from another file, declare the variable with IMPORT once at the beginning of the file and once in each function or block where you want to use the variable. For example,
| Before first function */ IMPORT x; ... void func1( void ) { IMPORT x; ... }
The first IMPORT declaration adds the necessary extern specifier and clearly indicates that x is defined in another file and that x may be used in other files (as well as in the current file and in the one that defines it). Like the previous example, the second IMPORT identifies a nonlocal variable used in a function. In this case, the two IMPORT declarations are clearly warning that func1 uses a variable that functions in other files also might use.
Finding Your C Legs
SHARE, EXPORT, and IMPORT let your declarations "say what they mean and mean what they say." You may prefer different macro names; almost any descriptive names produce clearer code than C's standard syntax. I picked up the idea for the macros from Steve Schustack's suggestion in Variations in C for similar macros he calls GLOBAL, SEMIGLOBAL, and IMPORT. I think EXPORT indicates better than GLOBAL the role of a variable allocated in one file and made available to other files, and SHARE seems more informative than SEMIGLOBAL. But whatever names you choose, these simple "visibility" macros and guidelines for declarations will let you, and anyone who reads your programs, C clearly now.
C Coding Suggestions
Previous | Table of Contents | Next |