8.2. Declarations, Definitions and AccessibilityChapter 4 introduced the concepts of scope and linkage, showing how they can be combined to control the accessibility of things throughout a program. We deliberately gave a vague description of exactly what constitutes a definition on the grounds that it would give you more pain than gain at that stage. Eventually it has to be spelled out in detail, which we do in this chapter. Just to make things interesting, we need to throw in storage class too. You'll probably find the interactions between these various elements to be both complex and confusing: that's because they are! We try to eliminate some of the confusion and give some useful rules of thumb in Section 8.2.6 below—but to understand them, you still need to read the stuff in between at least once. For a full understanding, you need a good grasp of three distinct but related concepts. The Standard calls them:
and describes what they mean in a fairly readable way (for a standard). Scope and linkage have already been described in Chapter 4, although we do present a review of them below. 8.2.1. Storage class specifiersThere are five keywords under the category of storage class
specifiers, although one of them, Storage class specifiers help you to specify the type of storage used
for data objects. Only one storage class specifier is permitted in
a declaration—this makes sense, as there is only one way of storing
things—and if you omit the storage class specifier in
a declaration, a default is chosen. The default depends on whether the
declaration is made outside a function (external declarations) or inside
a function (internal declarations). For external declarations the
default storage class specifier will be The positioning of a declaration, the storage class specifiers used (or their defaults) and, in some cases, preceding declarations of the same name, can all affect the linkage of a name, although fortunately not its scope or duration. We will investigate the easier items first. 8.2.1.1. DurationThe duration of an object describes whether its storage is allocated once only, at program start-up, or is more transient in its nature, being allocated and freed as necessary. There are only two types of duration of objects: static duration and automatic duration. Static duration means that the object has its storage allocated permanently, automatic means that the storage is allocated and freed as necessary. It's easy to tell which is which: you only get automatic duration if
(if you work through the rules, you'll find that the formal parameters of a function always meet all three requirements—they are always ‘automatic’). Although the presence of Data objects declared inside functions are given the default storage
class specifier of The A final note on
#include <stdio.h>
#include <stdlib.h>
void func(register int arg1, double arg2);
main(){
func(5, 2);
exit(EXIT_SUCCESS);
}
/*
* Function illustrating that formal parameters
* may be declared to have register storage class.
*/
void func(register int arg1, double arg2){
/*
* Illustrative only - nobody would do this
* in this context.
* Cannot take address of arg1, even if you want to
*/
double *fp = &arg2;
while(arg1){
printf("res = %f\n", arg1 * (*fp));
arg1--;
}
}Example 8.1So, the duration of an object depends on the storage class specifier used, whether it's a data object or function, and the position (block or file scope) of the declaration concerned. The linkage is also dependent on the storage class specifier, what kind of object it is and the scope of the declaration. Table 8.1 and Table 8.2 show the resulting storage duration and apparent linkage for the various combinations of storage class specifiers and location of the declaration. The actual linkage of objects with static duration is a bit more complicated, so use these tables only as a guide to the simple cases and take a look at what we say later about definitions.
The table above omits the
Internal 8.2.2. ScopeNow we must look again at the scope of the names of objects, which defines when and where a given name has a particular meaning. The different types of scope are the following:
The easiest is function scope. This only applies to labels, whose names are visible throughout the function where they are declared, irrespective of the block structure. No two labels in the same function may have the same name, but because the name only has function scope, the same name can be used for labels in every function. Labels are not objects—they have no storage associated with them and the concepts of linkage and duration have no meaning for them. Any name declared outside a function has file scope, which means that the name is usable at any point from the declaration on to the end of the source code file containing the declaration. Of course it is possible for these names to be temporarily hidden by declarations within compound statements. As we know, function definitions must be outside other functions, so the name introduced by any function definition will always have file scope. A name declared inside a compound statement, or as a formal parameter
to a function, has block scope and is usable up to the end of
the associated A special and rather trivial example of scope is function prototype scope where a declaration of a name extends only to the end of the function prototype. That means simply that this is wrong (same name used twice): void func(int i, int i); and this is all right: void func(int i, int j); The names declared inside the parentheses disappear outside them. The scope of a name is completely independent of any storage class specifier that may be used in its declaration. 8.2.3. LinkageWe will briefly review the subject of linkage here, too.
Linkage is used to determine what makes the same name
declared in different scopes refer to the same thing. An object only
ever has one name, but in many cases we would like to be able to refer
to the same object from different scopes. A typical example is the wish
to be able to call The Standard warns that declarations which refer to the same thing must all have compatible type, or the behaviour of the program will be undefined. A full description of compatible type is given later; for the moment you can take it to mean that, except for the use of the storage class specifier, the declarations must be identical. It's the responsibility of the programmer to get this right, though there will probably be tools available to help you check this out. The three different types of linkage are:
In an entire program, built up perhaps from a number of source files and libraries, if a name has external linkage, then every instance of a that name refers to the same object throughout the program. For something which has internal linkage, it is only within a given source code file that instances of the same name will refer to the same thing. Finally, names with no linkage refer to separate things. 8.2.4. Linkage and definitionsEvery data object or function that is actually used in a program
(except as the operand of a This ‘exactly one’ rule means that for objects with external linkage there must be exactly one definition in the whole program; for things with internal linkage (confined to one source code file) there must be exactly one definition in the file where it is declared; for things with no linkage, whose declaration is always a definition, there is exactly one definition as well. Now we try to draw everything together. The real questions are
We need to look into linkage first, then definitions. How do you get the appropriate linkage for a particular name? The rules are a little complicated.
These rules were used to derive the ‘linkage’ columns of Table 8.1 and Table 8.2, without the full application of rule 2—hence the use of the ‘probably external’ term. Rule 2 allows you to determine the precise linkage in those cases. What makes a declaration into a definition?
A consequence of the foregoing is that unless you also provide an initializer, declarations that explicitly include the extern storage class specifier do not result in a definition. 8.2.5. Realistic use of linkage and definitionsThe rules that determine the linkage and definition associated with declarations look quite complicated. The combinations used in practice are nothing like as bad; so let's investigate the usual cases. The three types of accessibility that you will want of data objects or functions are:
For the three cases above, you will want external linkage, internal linkage, and no linkage respectively. The recommended practice for the first two cases is to declare all of the names in each of the relevant source files before you define any functions. The recommended layout of a source file would be as shown in Figure 8.1. The external linkage declarations would be prefixed with extern, the
internal linkage declarations with
/* example of a single source file layout */
#include <stdio.h>
/* Things with external linkage:
* accessible throughout program.
* These are declarations, not definitions, so
* we assume their definition is somewhere else.
*/
extern int important_variable;
extern int library_func(double, int);
/*
* Definitions with external linkage.
*/
extern int ext_int_def = 0; /* explicit definition */
int tent_ext_int_def; /* tentative definition */
/*
* Things with internal linkage:
* only accessible inside this file.
* The use of static means that they are also
* tentative definitions.
*/
static int less_important_variable;
static struct{
int member_1;
int member_2;
}local_struct;
/*
* Also with internal linkage, but not a tentative
* definition because this is a function.
*/
static void lf(void);
/*
* Definition with internal linkage.
*/
static float int_link_f_def = 5.3;
/*
* Finally definitions of functions within this file
*/
/*
* This function has external linkage and can be called
* from anywhere in the program.
*/
void f1(int a){}
/*
* These two functions can only be invoked by name from
* within this file.
*/
static int local_function(int a1, int a2){
return(a1 * a2);
}
static void lf(void){
/*
* A static variable with no linkage,
* so usable only within this function.
* Also a definition (because of no linkage)
*/
static int count;
/*
* Automatic variable with no linkage but
* an initializer
*/
int i = 1;
printf("lf called for time no %d\n", ++count);
}
/*
* Actual definitions are implicitly provided for
* all remaining tentative definitions at the end of
* the file
*/Example 8.2We suggest that your re-read the preceding sections to see how the rules have been applied in Example 8.2. |
The C BookThis book is published as a matter of historical interest. Please read the copyright and disclaimer information. GBdirect Ltd provides up-to-date training and consultancy in C, Embedded C, C++ and a wide range of other subjects based on open standards if you happen to be interested. |
|||||||||||||||||||||||||||||||||||||||||||||||||
West Yorkshire Office
GBdirect Ltd
Training: 0800 651 0338 Please call between 0900 and 1700 (UK time) on Monday to Friday South East Regional Office
GBdirect Ltd
Training: 0800 651 0338 Please call between 0900 and 1700 (UK time) on Monday to Friday Please note: |