Previous | Table of Contents | Next |
Deck: Follow these tips for unlimited visibility in your C declarations
by Paul Conte
An object's scope is something you can't C very clearly in source code. Or should I say you can't code scope clearly in C source? If you can't quite C where I'm headed, it's because we're just beginning a winding tour through the maze of C object visibility. Follow closely, and by the time we exit the maze, you'll have a simple map for the shortest route out.
C, like many languages, lets an identifier x refer to different objects (e.g., storage locations for variables) at different places in the source code. As a simple case, the two declarations of x below refer to different variables.
int x; main( void ) { int x; ... } void func1( void ) { ... }
Let's say the first x refers to a storage location we'll call S1, and the second x refers to a storage location we'll call S2. The scope of the S1 object (storage location) is simply the region (i.e., lines) of source code where references to x are references to S1; likewise, the scope of the S2 object is the region of source code where references to x are references to S2. In this example, the scope of S1 (the first x) is everywhere outside the main function, and the scope of S2 (the second x) is only within the main function. Obviously, for the program to be clear, these two regions of the program can't overlap; each reference to x must refer to just one of the storage locations. Scope is also called "visibility" because you can "see" an object (e.g., read or change a storage location) only within its scope. In this chapter, I use "visibility" for the general concept and "scope" to refer to C's specific lexical scope attribute.
The C-nic Route
The concept of visibility is simple and useful. Among other uses, distinct regions of object visibility let you use identifiers in different parts of your code without worrying about whether the same identifier, used for different purposes, unintentionally refers to the same object. But why be merely simple and useful when you can be clever and brave, too? Take your first left turn into the C labyrinth.
C splits the single concept of visibility into scope and linkage, with scope referring to the region of program text within which an identifier's characteristics are understood. C's linkage term refers to the connection between identifiers in independently compiled translation units. Expressing visibility with two attributes instead of one may help C compiler writers, but it makes it more difficult for programmers to determine which object an identifier references. I'll try to map out C's rules while noting some language flaws that lead to C's problems. Then I'll mark an easy path to declaring variables and functions with the desired visibility.
C's rules for function visibility are simple: If you specify static storage class, a function is visible throughout the source file in which it's defined, but not in other source files. With extern or no storage class specifier, a function is visible throughout the program (i.e., across all files), and you can call it from anywhere. These rules lead to my first suggestion: Declare functions static if you intend to call them only from within the same source file.
C's rules for variable visibility are far more complex than those for function visibility. I've listed these rules in a table (Figure 5.1) that shows, for any variable declaration, where that variable is visible. I've also listed C's scope and linkage attributes and whether the declaration causes storage to be allocated (in C terminology, whether it is a definition as well as a declaration). You can use this guide to help you understand some of the mysterious changes that can occur when a C variable has unexpected visibility. You also may need it to follow my next few examples, but later I'll show you a far more useful table for C programming.
Where | Storage | Initial | Storage | Scope/Linkage | Visibility |
---|---|---|---|---|---|
specified | class | value | allocation | ||
1. Declared | (none) | Yes | Yes | Block scope, | Within same block, including all |
in block | auto | No | Yes | no linkage | nested blocks, except |
register | any nested block (and its | ||||
static | nested blocks) with an identical identifier without extern | ||||
2. Declared | extern | No | CASE A: Enclosing scope has identical, visible | ||
in block | identifier same scope, linkage, allocation, visibility as the matching identifier | ||||
CASE B: Otherwise, same as if declared extern outside function (see 7, below) | |||||
3. Declared | extern | Yes | ILLEGAL | ||
in block | declaration | ||||
4. Not declared but referenced in block | CASE A: Enclosing scope has identical, visible identifier declared in the same source file prior to the reference same as if declared extern in block (see 2, above) | ||||
CASE B: Otherwise, ILLEGAL declaration |
Which | Storage | Initial | Storage | Scope/Linkage | Visibility |
---|---|---|---|---|---|
declaration | class | value | allocation | ||
5. First | (none) | Yes | Yes | File scope | Rest of file except any |
declaration | No | external | block (and its nested blocks) with an identical identifier declared without extern, and blocks it contains and Other files with an identical external linkage identifier declared in them (No other file may allocate (define) an identical external linkage identifier.) | ||
6. First | static | Yes | Yes | File scope, | Rest of file except any |
declaration | No | internal linkage | block (and its nested blocks) with an identical identifier declared without extern, and blocks it contains | ||
7. First | extern | Yes | Yes | File scope, | Rest of life except any |
declaration | external linkage | block (and its nested blocks) with an identical identifier declared without extern, and blocks it contains and Other files with an identical external linkage identifier declared in them (One other file must have an identical external linkage identifier allocated (defined) in it.) | |||
8. First | extern | Yes | Yes | Same as if declared outside function | |
declaration | without extern or static (see 5, above) | ||||
9. Second or | Must have same | Same scope, linkage, allocation, and | |||
later | type and linkage | visibility as first declaration | |||
declaration | as first declaration |
Previous | Table of Contents | Next |