United States |
|
|
||
2.11.2.1 Example---Using the inline Function SpecifierConsider the following C code, which results in a multiply defined function identifier ( my_max ):
One way around this problem is to define the function my_max with the keyword static :
However, this means there is no globally visible my_max function but, rather, a copy of my_max for each module, each copy with a different address. Therefore, any function pointer comparisons would break. The ISO C99 solution to this problem is the inline keyword. Adding inline to the header file t.h eliminates the MULDEF errors:
This type of function definition, like one specified with the __inline keyword, marks the function for potential inlining by the compiler. One difference, however, is that for an inline function, the compiler creates an inline auxiliary definition of the function, which is associated with the function being declared ( my_max in this example). The compiler is then free to do one of the following:
There can be one and only one global definition for the inline function within an application. There can be one inline auxiliary definition per module, or many prototype declarations of the auxiliary function per module. You can create a global inline definition by including in one of your modules (such as a.c in our example) a file-scope function declaration that:
2.11.3 The __forceinline ModifierSimilar to the __inline storage-class modifier, the __forceinline storage-class modifier marks a function for inline expansion. However, using __forceinline on a function definition and prototype tells the compiler that it must substitute the code within the function definition for every call to that function. (With __inline , such substitution occurs at the discretion of the compiler.) On OpenVMS VAX systems, the __forceinline storage-class modifier does not cause any more inlining to occur than the __inline modifier does. Use the following form to designate a function for forced inline expansion:
The compiler issues a warning if
__forceinline
is used in /STANDARD=PORTABLE mode, because this is an
implementation-specific extension.
The __align and _align storage-class modifiers have the same semantic meaning. The difference is that __align is a keyword in all compiler modes while _align is a keyword only in modes that recognize VAX C keywords. For new programs, using __align is recommended. The __align storage-class modifier aligns objects of any of the Compaq C data types on a specified storage boundary. Use the __align modifier in a data declaration or definition. For example, to align an integer on the next quadword boundary, you can use any of the following declarations:
When specifying the boundary of the data alignment, you can either use a predefined constant or specify an integer value that is a power of 2. These constants, or explicit powers of 2, tell Compaq C the number of bytes to pad in order to align the data. In the previous example, int __align ( 3 ) specifies an alignment of 23 bytes, which is 8 bytes---a quadword of memory. Table 2-1 presents all the predefined alignment constants, their equivalent power of 2, and their equivalent number of bytes. Note that for OpenVMS VAX systems, you can specify the constants 0, 1, 2, 3, 4, or 9. For OpenVMS Alpha systems, you can specify any constant from 0 to 16.
2.12 Forward ReferencesOnce declared, identifiers can be used freely. Using an identifier before its declaration is called a forward reference, and results in an error, except in the following cases:
Here are some examples of valid and invalid forward references:
The following example shows the use of a structure tag in a forward reference:
2.13 TagsTags can be used with structures, unions, or enumerated types as a means of referring to the structure, union, or enumerated type elsewhere in the program. Once a tag is included in the declaration of a structure, union, or enumerated type, it can specify the declared structure, union, or enumerated type anywhere the declaration is visible. The following code fragment shows the use of a structure tag, a union tag, and an enumerated type tag:
As shown in the previous example, once a tag is declared it can be used to reference other structure, union, or enumerated type declarations in the same scope without fully redefining the object. Tags can be used to form an incomplete type if they occur before the complete declaration of a structure or union. Incomplete types do not specify the size of the object; therefore, a tag introducing an incomplete type can only be used when the size of the object is not needed. To complete the type, another declaration of the tag in the same scope must define the object completely. The following example shows how a subsequent definition completes the incomplete declaration of the structure type s :
Section 2.6 describes the concept of an incomplete type. Consider the following declarations:
These declarations specify a structure or union type and declare a tag visible only within the scope of the declaration. The declaration specifies a new type distinct from any other type with the same tag in an enclosing scope (if any). The following example shows the use of prior tag declarations to specify a pair of mutually-referential structures:
If s2 was declared as a tag in an enclosing scope, the declaration D1 would refer to s2 , not to the tag s2 declared in D2 . To eliminate this context sensitivity, the following declaration can be inserted ahead of D1 :
This declares a new tag
s2
in the inner scope; the declaration
D2
then completes the specification of the type.
An rvalue is the value of an expression, such as 2, or x + 3 , or (x + y) * (a - b) . rvalues are not allocated storage space. Examples of rvalues are the numbers 0 and 1 in the following code fragment:
The identifiers x and y are objects with allocated storage. The pointer to y holds an lvalue. An lvalue is an expression that describes the location of an object used in the program. The location of the object is the object's lvalue, and the object's rvalue is the value stored at the location described by the lvalue. The following operators always produce lvalues:
The dot operator ( . ) can, and usually does, produce an lvalue but it does not have to do so. For example, f().m is not an lvalue.
A modifiable lvalue is an lvalue that does not have array
type, an incomplete type, a
const
-qualified type, or, if it is a structure or union, has no member with
const
-qualified type.
Name spaces are identifier classifications based on the context of the identifier's use in the program. Name spaces allow the same identifier to simultaneously stand for an object, statement label, structure tag, union member, and enumeration constant. Simultaneous use of an identifier in the same scope for two different entities without ambiguity is possible only if the identifiers are in different name spaces. The context of the identifier's use resolves the ambiguity over which of the identically named entities is desired. There are four different name spaces:
For example, the identifier flower can be used in one block to stand for both a variable and an enumeration tag, because variables and tags are in different name spaces. Subsequently, an inner block can redefine the variable flower without disturbing the enumeration tag flower . Therefore, when using the same identifier for various purposes, analyze the name space and scope rules governing the identifier. Section 2.3 presents the scope rules.
A structure, union, and enumeration member name can be common to each
of these objects at the same time. The use of the structure, union, or
enumeration name in the reference to the member resolves any ambiguity
about which identifier is meant. However, the structure, union, or
enumeration tag must be unique, since the tags of these three object
types share the same name space.
The translation of a C program occurs in several phases. Normally, when the compiler is started, several events occur before the actual compiler starts:
The fourth step is called preprocessing, and is handled by a separate unit of the compiler. Each preprocessor directive appears on a line beginning with a pound sign (#); white space may precede the pound sign. These lines are syntactically independent from the rest of the C source file, and can appear anywhere in the source file. Preprocessor directive lines terminate at the end of the logical line.
It is possible to preprocess a source file without actually compiling
the program (see your platform-specific Compaq C documentation for
the available compiler options.) Chapter 8 discusses the
preprocessing directives.
In several contexts a type name can or must be specified without an identifier. For example, in a function prototype declaration, the parameters of the function can be declared only with a type name. Also, when casting an object from one type to another, a type name is required without an associated identifier. ( Section 6.4.6 has information on casting, and Section 5.5 has information on function prototypes.) This is accomplished using a type name, which is a declaration for a function or object which omits the identifier. Table 2-2 shows examples of type names with the associated types they refer to.
Table 2-2 also provides good examples of abstract declarators. An abstract declarator is a declarator without an identifier. The characters following the int type name form an abstract declarator in each case. The * , [ ] , and ( ) characters all indicate a declarator without naming a specific identifier.
| |
privacy statement and legal notices |