CATEGORII DOCUMENTE |
Asp | Autocad | C | Dot net | Excel | Fox pro | Html | Java |
Linux | Mathcad | Photoshop | Php | Sql | Visual studio | Windows | Xml |
Chapter 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 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 although we do present a review of them below.
There are five keywords under the category of storage class specifiers,
although one of them, typedef
,
is there more out of convenience than utility; it has its own section later
since it doesn't really belong here. The ones remaining are auto
, extern
,
register
, and static
.
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 extern
and for internal
declarations it will be auto
. The only exception to this rule is
the declaration of functions, whose default storage class specifier is
always extern
.
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.
The 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
static
or extern
keywords (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 static
in a declaration unambiguously ensures that it has static duration, it's
interesting to see that it is by no means the only way. This is a notorious
source of confusion, but we just have to accept it.
Data objects declared inside functions are given the default storage class
specifier of auto
unless
some other storage class specifier is used. In the vast majority of cases, you
don't want these objects to be accessible from outside the function, so you
want them to have no linkage. Either the
default, auto
, or the
explicit register storage
class specifier results in an object with no linkage and automatic
duration. Neither auto
nor register
can be applied
to a declaration that occurs outside a function.
The register
storage
class is quite interesting, although it is tending to fall into disuse nowadays.
It suggests to the compiler that it would be a good idea to store the object in
one or more hardware registers in the interests of speed. The compiler does not
have to take any notice of this, but to make things easy for it, register
variables do not have an
address (the &
address-of operator is forbidden) because some computers don't support the idea
of addressable registers. Declaring too many register
objects may slow the program down, rather than speed it up, because the
compiler may either have to save more registers on entrance to a function,
often a slow process, or there won't be enough registers remaining to be used
for intermediate calculations. Determining when to use registers will be a
machine-specific choice and should only be taken when detailed measurements
show that a particular function needs to be speeded up. Then you will have to
experiment. In our opinion, you should never declare register variables during
program development. Get the program working first, then measure it, then, maybe,
judicious use of registers will give a useful increase in performance. But that
work will have to be repeated for every type of processor you move the program
to; even within one family of processors the characteristics are often
different.
A final note on register
variables: this is the only storage class specifier that may be used in a
function prototype or function definition. In a function prototype, the storage
class specifier is simply ignored, in a function
definition it is a hint that the actual parameter should be stored in a
register if possible. This example shows how it might be used:
Example 8.1
So, 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.
Storage Class Specifier |
Function or Data Object |
Linkage |
Duration |
|
either |
internal |
static |
|
either |
probably external |
static |
none |
function |
probably external |
static |
none |
data object |
external |
static |
Table 8.1. External declarations (outside a function)
The table above omits the register
and auto
storage class
specifiers because they are not permitted in file-scope (external)
declarations.
Storage Class Specifier |
Function or Data Object |
Linkage |
Duration |
|
data object only |
none |
automatic |
|
data object only |
none |
automatic |
|
data object only |
none |
static |
|
either |
probably external |
static |
none |
data object |
none |
automatic |
none |
function |
probably external |
static |
Table Internal declarations
Internal static
variables
retain their values between calls of the function that contains them, which is
useful in certain circumstances (see Chapter .
Now 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
which closes
the compound statement. Any declaration of a name within a compound statement
hides any outer declaration of the same name until the end of the compound
statement.
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.
We 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 printf
from several different places in a program, even
if those places are not all in the same source file.
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.
Every data object or function that is actually used in a program (except as
the operand of a sizeof
operator) must have one and only one corresponding definition.
This is actually very important, although we haven't really come across it yet
because most of our examples have used only data objects with automatic
duration, whose declarations are axiomatically definitions, or functions which
we have defined by providing their bodies.
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.
static
must be at file
scope, outside any block) extern
storage class
specifier, or is the declaration of a function with no storage class
specifier (or both), then: 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?
static
storage class
specifier is a tentative definition. If a source code file
contains one or more tentative definitions for an object, then if that
file contains no actual definitions, a default definition is provided for
that object as if it had an initializer of
. (Structures and arrays have all
their elements initialized to
).
Functions do not have tentative definitions. 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.
The 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.
Figure 8.1. Layout of a source file
The external linkage declarations would be prefixed with extern, the
internal linkage declarations with static
.
Here's an example.
Example 8.2
We suggest that your re-read the preceding sections to see how the rules have been applied in Example
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 736
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved