Porting to ANSI Mode HP C [ HP C Programmer's Guide ] MPE/iX 5.0 Documentation
HP C Programmer's Guide
Porting to ANSI Mode HP C
This section describes porting non-ANSI mode HP C programs to ANSI C.
Specifically, it discusses:
* Compile line options.
* ANSI C name spaces.
* Differences that can lead to porting problems.
ANSI Mode Compile Option (-Aa)
To compile in ANSI C mode, use the -Aa compile time option. By default,
HP C compilers use non-ANSI mode; that is HP C compilers use the language
definition defined in Kernighan and Ritchie's The C Programming Language,
First Edition, as well as selected BSD (Berkeley Software Distribution)
extensions. ANSI mode may become the default in a future release.
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.
HP C Extensions to ANSI C (+e)
There are a number of HP C extensions enabled by the +e option in ANSI
mode:
* Long pointers.
* Dollar sign character $ in an identifier.
* Compiler supplied defaults for missing arguments to intrinsic
calls (For example FOPEN("filename",fopt,,rsize), where ,,
indicates that the missing aopt parameter is automatically
supplied with default values.)
* Sized enumerated types: char enum, short enum, int enum, and long
enum.
* Long long integer type. Note, the long long data type is only
available in HP C Series 700/800.
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.
const and volatile Qualifiers
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:
const float pi = 3.14;
A const variable can be used like any other variable. For example:
area = pi * (radius * radius);
But attempting to assign a value to a const variable causes a compile
error:
pi = 3.1416; /* This causes an 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:
char *const prompt = "Press return to continue> ";
An attempt to reassign the const pointer prompt causes a compiler error.
For example:
prompt = "Exiting program."; /* Causes a compile time error. */
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:
volatile int intlist[100];
volatile char *revision_level;
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/UX Reference Manual or the HP
C/iX Reference Manual.
ANSI Mode Function Prototypes
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:
* Better type checking between declarations and calls because the
number and types of the parameters are part of the function's
parameter list. For example:
struct s
{
int i;
}
int old_way(x)
struct s x;
{
/* Function body using the old method for
declaring function parameter types
*/
}
int new_way(struct s x)
{
/* Function body using the new method for
declaring function parameter types
*/
}
/* The functions "old_way" and "new_way" are
both called later on in the program.
*/
old_way(1); /* This call compiles without complaint. */
new_way(1); /* This call gives an error. */
In this example, the function new_way gives an error because the
value being passed to it is of type int instead of type struct x.
* More efficient parameter passing in some cases. Parameters of
type float are not converted to double. For example:
void old_way(f)
float f;
{
/* Function body using the old method for
declaring function parameter types
*/
}
void new_way(float f)
{
/* Function body using the new method for
declaring function parameter types
*/
}
/* The functions "old_way" and "new_way" are
both called later on in the program.
*/
float g;
old_way(g);
new_way(g);
In the above example, when the function old_way is called, the
value of g is converted to a double before being passed. In ANSI
mode, the old_way function then converts the value back to float.
When the function new_way is called, the float value of g is
passed without conversion.
* Automatic conversion of function arguments, as if by assignment.
For example, integer parameters may be automatically converted to
floating point.
/* Function declaration using the new method
for declaring function parameter types
*/
extern double sqrt(double);
/* The function "sqrt" is called later
on in the program.
*/
sqrt(1);
In this example, any value passed to sqrt is automatically
converted to double.
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.
Mixing Old-Style Function Definitions with ANSI Function 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:
void func1(char c);
void func1(c)
char c;
{ }
gets the following message when compiled in ANSI mode:
Inconsistent parameter list declaration for "func1"
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:
void func1(int c);
fixes the error.
Function Prototype Considerations.
There are three things to consider when using function prototypes:
* Type differences between actual and formal parameters.
* Declarations of a structure in a prototype parameter.
* Mixing of const and volatile qualifiers and function prototypes.
Type Differences between Actual and Formal Parameters.
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:
func1(){
float f;
func2(f);
}
int func2(float arg1){
/* body of func2 */
}
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:
Inconsistent parameter list declaration for "func2".
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.
Declaration of a Structure in a Prototype Parameter.
Another potential prototype problem occurs when structures are declared
within a prototype parameter list. The following example illustrates a
problem that may arise:
func3(struct stname *arg);
struct stname { int i; };
void func4(void) {
struct stname s;
func3(&s);
}
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:
types in call and definition of 'func3' have incompatible
struct/union pointer types for parameter 'arg'
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 of const and volatile Qualifiers and Function Prototypes.
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:
/* pointer to pointer to int */
int **actual0;
/* const pointer to pointer to int */
int **const actual1;
/* const pointer to const pointer to int */
int *const *const actual2;
/* const pointer to const pointer to const int */
const int *const *const actual3;
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:
void f0(int **formal0);
void f1(int **const formal1);
void f2(int *const *const formal2);
void f3(const int *const *const formal3);
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:
--------------------------------------------
| | | | | |
| | f0 | f1 | f2 | f3 |
| | | | | |
--------------------------------------------
| | | | | |
| actual0 | C | C | C | N |
| | | | | |
--------------------------------------------
| | | | | |
| actual1 | C | C | C | N |
| | | | | |
--------------------------------------------
| | | | | |
| actual2 | S | S | C | N |
| | | | | |
--------------------------------------------
| | | | | |
| actual3 | NS | NS | N | C |
| | | | | |
--------------------------------------------
C = compatible
S = not compatible, qualifier level two of formal is not a superset of
actual parameter
N = not compatible, qualifier level three doesn't match
MPE/iX 5.0 Documentation