Interdata_v6/usr/doc/ctut/ct7
.NH
Scope Rules: Who Knows About What
.PP
A complete C program need not be compiled all at once;
the source text of the program may be kept in several files,
and previously compiled routines may be loaded from libraries.
How do we arrange that data gets passed from one
routine to another?
We have already seen how to use function arguments and values,
so let us talk about external data.
Warning: the words
.ul
declaration
and
.ul
definition
are used precisely in this section;
don't treat them as the same thing.
.PP
A major shortcut exists for making
.UL extern
declarations.
If the definition of a variable appears
.ul
before
its use in some function,
no
.UL extern
declaration is needed within the function.
Thus, if a file contains
.E1
f1( ) { \*.\*.\*. }
.SP
int foo;
.SP
f2( ) { \*.\*.\*. foo = 1; \*.\*.\*. }
.SP
f3( ) { \*.\*.\*. if ( foo ) \*.\*.\*. }
.E2
no declaration of
.UL foo
is needed in either
.UL f2
or or
.UL f3,
because the external definition of
.UL foo
appears before them.
But if
.UL f1
wants to use
.UL foo,
it has to contain the declaration
.E1
f1( ) {
extern int foo;
\*.\*.\*.
}
.E2
.PP
This is true also of any function that exists
on another file _
if it wants
.UL foo
it has to use an
.UL extern
declaration
for it.
(If somewhere there is an
.UL extern
declaration for something,
there must also eventually be an external definition of it,
or you'll get an ``undefined symbol'' message.)
.PP
There are some hidden pitfalls in external declarations
and definitions if you use multiple source files.
To avoid them,
first,
define and initialize each external variable only once in the entire set of files:
.E1
int foo 0;
.E2
You can get away with multiple external definitions on
.UC UNIX,
but not on
.UC GCOS,
so don't ask for trouble.
Multiple initializations are illegal everywhere.
Second,
at the beginning of any file that contains functions needing a variable
whose definition is in some other file,
put in an
.UL extern
declaration,
outside of any function:
.E1
extern int foo;
.SP
f1( ) { \*.\*.\*. }
etc\*.
.E2
.PP
The
.UL #include
compiler control line,
to be discussed shortly,
lets you make a single copy of the external declarations
for a program and then stick them into each of the source files
making up the program.
.NH
#define, #include
.PP
C provides a very limited macro facility.
You can say
.E1
#define name something
.E2
and thereafter anywhere ``name'' appears as a token,
``something'' will be substituted.
This is particularly useful in parametering the sizes of arrays:
.E1
#define ARRAYSIZE 100
int arr[ARRAYSIZE];
\*.\*.\*.
while( i\*+ < ARRAYSIZE )\*.\*.\*.
.E2
(now we can alter the entire program by changing only the
.UL define)
or in setting up mysterious constants:
.E1
#define SET 01
#define INTERRUPT 02 /\** interrupt bit \**/
#define ENABLED 04
\*.\*.\*.
if( x & (SET | INTERRUPT | ENABLED) ) \*.\*.\*.
.E2
Now we have meaningful words instead of mysterious constants.
(The mysterious operators `&' (AND)
and `\(or' (OR)
will be covered in the next section.)
It's an excellent practice to write programs
without any literal constants except in
.UL #define
statements.
.PP
There are several warnings about
.UL #define\*.
First, there's no semicolon at the end of a
.UL #define;
all the text from the name to the end of the line
(except for comments)
is taken to be the ``something''.
When it's put into the text, blanks are placed around it.
Good style typically makes the name in the
.UL #define
upper case _
this makes parameters more visible.
Definitions affect things only after they occur,
and only within the file in which they occur.
.MC
Defines can't be nested.
.mc
.WS
.PP
The other control word known to C is
.UL #include\*.
To include one file in your source at compilation time, say
.E1
#include "filename"
.E2
This is useful for putting a lot of heavily used data definitions and
.UL #define
statements at the beginning of a file to be compiled.
.MC
.UL #include
can be nested _
an included file may contain another
.UL #include
to bring in another file.
.mc