|
|
HP C/HP-UX Programmer's Guide: HP 9000 Computers > Chapter 5 Programming for Portability Porting to ANSI Mode HP C |
|
This section describes porting non-ANSI mode HP C programs to ANSI C. Specifically, it discusses:
To compile in ANSI C mode, use the -Aa compile time option. By default, beginning at the HP-UX 10.30 operating system release, HP C compilers use -Ae. The -w and +e options should not be used at compile time for true ANSI compliance. These options suppress warning messages and allow HP C extensions that are not ANSI conforming. There are a number of HP C extensions enabled by the +e option in ANSI mode:
These are the only HP C extensions that require using the +e option. When coding for portability, you should compile your programs without the +e command line option, and rewrite code that causes the compiler to generate messages related to HP C extensions. HP C supports the ANSI C const and volatile keywords used in variable declarations. These keywords qualify the way in which the compiler treats the declared variable. The const qualifier declares variables whose values do not change during program execution. The HP C compiler generates error messages if there is an attempt to assign a value to a const variable. The following declares a constant variable pi of type float with an initial value of 3.14:
A const variable can be used like any other variable. For example:
But attempting to assign a value to a const variable causes a compile error:
Only obvious attempts to modify const variables are detected. Assignments made using pointer references to const variables may not be detected by the compiler. However, pointers may be declared using the const qualifier. For example:
An attempt to reassign the const pointer prompt causes a compiler error. For example:
The volatile qualifier provides a way to tell the compiler that the value of a variable may change in ways not known to the compiler. The volatile qualifier is useful when declaring variables that may be altered by signal handlers, device drivers, the operating system, or routines that use shared memory. It may also prevent certain optimizations from occurring. The optimizer makes assumptions about how variables are used within a program. It assumes that the contents of memory will not be changed by entities other than the current program. The volatile qualifier forces the compiler to be more conservative in its assumptions regarding the variable. The volatile qualifier can also be used for regular variables and pointers. For example:
For further information on the HP C optimizer and its assumptions, see Chapter 4 “Optimizing HP C Programs ”. For further information on the const and volatile qualifiers see the HP C/HP-UX Reference Manual. Function prototypes are function declarations that contain parameter type lists. Prototype-style function declarations are available only in ANSI mode. You are encouraged to use the prototype-style of function declarations. Adding function prototypes to existing C programs yields three advantages:
Compiling an existing program in ANSI mode yields some of these advantages because of the existence of prototypes in the standard header files. To take full advantage of prototypes in existing programs, change old-style declarations (without prototype) to new style declarations. On HP-UX, the tool protogen (see protogen(1) in the on-line man pages) helps add prototypes to existing programs. For each source file, protogen can produce a header file of prototypes and a modified source file that includes prototype declarations. A common pitfall when mixing prototypes with old-style function definitions is to overlook the ANSI rule that for parameter types to be compatible, the parameter type in the prototype must match the parameter type resulting from default promotions applied to the parameter in the old-style function definition. For example:
gets the following message when compiled in ANSI mode:
The parameter type for c in the prototype is char. The parameter type for c in the definition func1 is also char, but it expects an int because it is an old-style function definition and in the absence of a prototype, char is promoted to int. Changing the prototype to:
fixes the error. The ANSI C standard does not require a compiler to do any parameter type checking if prototypes are not used. Value parameters whose sizes are larger than 64 bits (8 bytes) will be passed via a short pointer to the high-order byte of the parameter value. The receiving function then makes a copy of the parameter pointed to by this short pointer in its own local memory. There are three things to consider when using function prototypes:
When a prototype to a function is added, be careful that all calls to that function occur with the prototype visible (in the same context). The following example illustrates problems that can arise when this is not the case:
In the example above, when the call to func2 occurs, the compiler behaves as if func2 had been declared with an old-style declaration int func2(). For an old-style call, the default argument promotion rules cause the parameter f to be converted to double. When the declaration of func2 is seen, there is a conflict. The prototype indicates that the parameter arg1 should not be converted to double, but the call in the absence of the prototype indicates that arg1 should be widened. When this conflict occurs within a single file, the compiler issues an error:
This error can be fixed by either making the prototype visible before the call, or by changing the formal parameter declaration of arg1 to double. If the declaration and call of func2 were in separate files, then the compiler would not detect the mismatch and the program would silently behave incorrectly. On HP-UX, the lint(1) command can be used to find such parameter inconsistencies across files. Another potential prototype problem occurs when structures are declared within a prototype parameter list. The following example illustrates a problem that may arise:
In this example, the call and declaration of func3 are not compatible because they refer to different structures, both named stname. The stname referred by the declaration was created within prototype scope. This means it goes out of scope at the end of the declaration of func3. The declaration of stname on the line following func3 is a new instance of struct stname. When conflicting structures are detected, the compiler issues an error:
This error can be fixed by switching the first two lines and thus declaring struct stname prior to referencing it in the declaration of func3. Mixing the const and volatile qualifiers and prototypes can be tricky. Note that this section uses the const qualifier for all of its examples; however, you could just as easily substitute the volatile qualifier for const. The rules for prototype parameter passing are the same as the rules for assignments. To illustrate this point, consider the following declarations:
These declarations show how successive levels of a type may be qualified. The declaration for actual0 has no qualifiers. The declaration of actual1 has only the top level qualified. The declarations of actual2 and actual3 have two and three levels qualified. When these actual parameters are substituted into calls to the following functions:
The compatibility rules for pointer qualifiers are different for all three levels. At the first level, the qualifiers on pointers are ignored. At the second level, the qualifiers of the formal parameter must be a superset of those in the actual parameter. At levels three or greater the parameters must match exactly. Substituting actual0 through actual3 into f0 through f3 results in the following compatibility matrix: Table 5-1 Compatibility Rules for Pointer Qualifiers[1]
|
|