HP OpenVMS Systems

HP CLanguage Reference Manual

Because compound literals are unnamed, a single compound literal cannot
specify a circularly linked object. In this example, there is no way to
write a self-referential compound literal that could be used as the
function argument in place of the named object
endless_zeros
.

As shown in this example, each compound literal creates only a single *
object in a given scope.

The function
f()
always returns the value 1.

6.11 Data-Type Conversions

C performs data-type conversions in the following four situations:

When two or more operands of different types appear in an
expression.

When arguments of type
char
,
short
, and
float
are passed to a function using the old style declaration.

When arguments that do not conform exactly to the parameters
declared in a function prototype are passed to a function.

When the data type of an operand is deliberately converted by the
cast operator. See Section 6.4.6 for more information on the cast
operator.

The following sections describe how operands and function arguments are
converted.

6.11.1 Usual Arithmetic Conversions

The following rules---referred to as the usual arithmetic
conversions---govern the conversion of all operands in arithmetic
expressions. The effect is to bring operands to a common type, which is
also the type of the result. The rules govern in the following order:

If either operand is not of arithmetic type, no conversion is
performed.

If either operand has type
long double
, the other operand is converted to
long double
.

Otherwise, if either operand has type
double
, the other operand is converted to
double
.

Otherwise, if either operand has type
float
, the other operand is converted to
float
.

For complex numbers ( (ALPHA, I64)), the above conversions take
place for the corresponding real type of the operand to be converted.
For example, adding a
double _Complex
and a
float
, converts just the
float
operand to
double
, yielding a
double _Complex
result.

Otherwise, the integer promotions are performed on both operands,
and the following rules apply:

If both operands have the same type, then no further conversion is
needed.

Otherwise, if both operands have signed integer types or both have
unsigned integer types, the operand with the type of lesser integer
conversion rank is converted to the type of the operand with greater
rank.

Otherwise, If either operand has type
unsigned long long int
, the other operand is converted to
unsigned long long int
.

If either operand has type
unsigned long int
, the other operand is converted to
unsigned long int
.

Otherwise, if one operand has type
long int
and the other has type
unsigned int
, and if a
long int
can represent all values of an
unsigned int
, the operand of type
unsigned int
is converted to
long int
. If a
long int
cannot represent all the values of an
unsigned int
, both operands are converted to
unsigned long int
.

Otherwise, if either operand has type
long int
, the other operand is converted to
long int
.

Otherwise, if either operand has type
unsigned int
, the other operand is converted to
unsigned int
.

Otherwise, both operands have type
int
.

The following sections elaborate on the usual arithmetic conversion
rules.

6.11.1.1 Characters and Integers

A
char
,
short int
, or
int
bit field, either signed or unsigned, or an object that has enumeration
type, can be used in an expression wherever an
int
or
unsigned int
is permitted. If an
int
can represent all values of the original type, the value is converted
to an
int
. Otherwise, it is converted to an
unsigned int
. These conversion rules are called the integer promotions.

This implementation of integer promotion is called value
preserving, as opposed to unsigned preserving in which
unsigned char
and
unsigned short
widen to
unsigned int
. HP C uses value-preserving promotions, as required by the
ANSI C standard, unless the common C mode is specified.

To help locate arithmetic conversions that depend on unsigned
preserving rules, HP C, with the check option enabled, flags
any integer promotions of
unsigned char
and
unsigned short
to
int
that could be affected by the value-preserving approach for integer
promotions.

All other arithmetic types are unchanged by the integer promotions.

In HP C, variables of type
char
are bytes treated as signed integers. When a longer integer is
converted to a shorter integer or to
char
, it is truncated on the left; excess bits are discarded. For example:

6.11.1.2 Signed and Unsigned Integers

When a value with an integral type is converted to another integral
type (such as
int
converted to
long int
) and the value can be represented by the new type, the value is
unchanged.

When a signed integer is converted to an unsigned integer of equal or
greater size, and the signed integer value is nonnegative, its value is
unchanged. If the signed integer value is negative, then:

If the unsigned integer type is larger, the signed integer is first
promoted to the signed integer that corresponds to the unsigned
integer; then the value is converted to unsigned by adding to it one
greater than the largest number that can be represented in the unsigned
integer type.

If the unsigned integer type is equal or smaller than the signed
integer type, then the value is converted to unsigned by adding to it
one greater than the largest number that can be represented in the
unsigned integer type.

When an integer value is demoted to an unsigned integer of smaller
size, the result is the nonnegative remainder of the value divided by
the number one greater than the largest representable unsigned value
for the new integral type.

When an integer value is demoted to a signed integer of smaller size,
or an unsigned integer is converted to its corresponding signed
integer, the value is unchanged if it is small enough to be represented
by the new type. Otherwise, the result is truncated; excess high-order
bits are discarded and precision is lost.

Conversion between integral types of the same size, whether signed or
unsigned, results in no machine-level representation change.

6.11.1.3 Floating and Integral

When a floating-type operand is converted to an integer, the fractional
part is discarded.

When a floating-type value is to be converted at compile time to an
integer or another floating type, and the result cannot be represented,
the compiler reports a warning in the following instances:

The conversion is to
unsigned int
and the result cannot be represented by the
unsigned int
type.

The conversion is to a type other than
unsigned int
, and the result cannot be represented by the
int
type.

When a value of integral type is converted to floating type, and the
value is in the range of values that can be represented, but not
exactly, the result of the conversion is either the next higher or next
lower value, whichever is the natural result of the conversion on the
hardware. See your HP C documentation for the conversion
result on your platform.

6.11.1.4 Floating Types

If an operand of type
float
appears in an expression, it is treated as a single-precision object
unless the expression also involves an object of type
double
or
long double
, in which case the usual arithmetic conversion applies.

When a
float
is promoted to
double
or
long double
, or a
double
is promoted to
long double
, its value is unchanged.

The behavior is undefined when a
double
is demoted to
float
, or a
long double
to
double
or
float
, if the value being converted is outside the range of values that can
be represented.

If the value being converted is inside the range of values that can be
represented, but not exactly, the result is rounded to either the next
higher or next lower representable
float
value.

6.11.2 Pointer Conversions

Although two types (for example,
int
and
long
) can have the same representation, they are still different types.
This means that a pointer to
int
cannot be assigned to a pointer to
long
without using a cast. Nor can a pointer to a function of one type be
assigned to a pointer to a function of a different type without using a
cast. In addition, pointers to functions that have different
parameter-type information, including the old-style absence of
parameter-type information, are different types. In these instances, if
a cast is not used, the compiler issues an error. Because there are
alignment restrictions on some target processors, access through an
unaligned pointer can result in a much slower access time or a machine
exception.

A pointer to
void
can be converted to or from a pointer to any incomplete or object type.
If a pointer to any incomplete or object type is converted to a pointer
to
void
and back, the result compares equal to the original pointer.

An integral constant expression equal to 0, or such an expression cast
to the
void *
type, is called a null pointer constant. If a null pointer
constant is assigned to or compared for equality with a pointer, the
constant is converted to a pointer of that type. Such a pointer is
called a null pointer, and is guaranteed to compare unequal to
a pointer to any object or function.

An array designator is automatically converted to a pointer to the
array type, and the pointer points to the first element of the array.

6.11.3 Function Argument Conversions

The data types of function arguments are assumed to match the types of
the formal parameters unless a function prototype declaration is
present. In the presence of a function prototype, all arguments in the
function invocation are compared for assignment compatibility to all
parameters declared in the function prototype declaration. If the type
of the argument does not match the type of the parameter but is
assignment compatible, C converts the argument to the type of the
parameter (see Section 6.11.1). If an argument in the function
invocation is not assignment compatible to a parameter declared in the
function prototype declaration, an error message is generated.

If a function prototype is not present, all arguments of type
float
are converted to
double
, all arguments of type
char
or
short
are converted to type
int
, all arguments of type
unsigned char
and
unsigned short
are converted to
unsigned int
, and an array or function name is converted to the address of the
named array or function. The compiler performs no other conversions
automatically, and any mismatches after these conversions are
programming errors.

A function designator is an expression that has function type.
Except when it is the operand of the
sizeof
operator or the unary & operator, a function designator with type
"function returning type" is converted to an expression that
has type "pointer to function returning type."

Chapter 7Statements

This section describes the following kinds of statements in the C
programming language. Except as indicated in this chapter, statements
are executed in the sequence in which they appear in a function body:

7.1 Labeled Statements

A label is an identifier used to flag a location in a program as the
target of a
goto
statement or
switch
statement. A label has the following syntax:

identifier : statement

case constant-expression : statementdefault : statement

The scope of the label is the containing function body. Variables can
have the same name as a label in the function because labels and
variables have different name spaces (see Section 2.15).

There are three kinds of labeled statements in C:

Any statement preceded by a label

A
case
statement

A
default
statement

The last two statements are discussed in Section 7.5.2 because they can
appear only within a
switch
statement.

7.2 Compound Statements

A compound statement, or block, allows a sequence of
statements to be treated as a single statement. A compound statement
begins with a left brace, contains any mix of declarations and
statements, and ends with a right brace, as shown in the following
example:

{
int a;
a = 1;
int b;
b = 2;
}

Note

The ability to mix declarations and statements in any sequence in a
compound statement is not allowed in common C, VAX C, and Strict ANSI89
modes. In these modes, the declarations must be specified first,
followed by the statements.

Block declarations are local to the block, and, for the rest of the
block, they supersede other declarations of the same name in outer
scopes.

A block is entered normally when control flows into it, or
when a
goto
statement transfers control to a label at the beginning of the block
itself. Each time the block is entered normally, storage is allocated
for
auto
or
register
variables. If, on the other hand, a
goto
statement transfers control to a label inside the block or if
the block is the body of a
switch
statement, these storage allocations do not occur. For more information
about storage classes, see Section 2.10.

Function definitions contain compound statements. The compound
statement following the parameter declarations in a function definition
is called the function body.

7.3 Expression Statements

Any valid expression can be used as a statement by following the
expression with a semicolon, as shown in the following example:

i++;

This statement increments the value of the variable
i
. Note that
i++
is a valid C expression that can appear in more complex C statements.
For more information about the C expressions, see Chapter 6.

7.4 Null Statements

A null statement is used to provide a null operation in situations
where the grammar of the language requires a statement, but the program
requires no work to be done. The null statement consists of a semicolon:

;

The null statement is useful with the
if
,
while
,
do
, and
for
statements. The most common use of this statement is in loop operations
in which all the loop activity is performed by the test portion of the
loop. For example, the following statement finds the first element of
an array that has a value of 0:

for (i=0; array[i] != 0; i++)
;

In this example, the
for
statement is executed for its side effects only; the loop body is a
null statement. See Section 7.6 for more information about iteration
statements.

The null statement is also useful where a label is needed just before a
brace that terminates a compound statement. (A label cannot immediately
precede the right brace; it must always be attached to a statement.)
For example:

7.5 Selection Statements

A selection statement selects among a set of statements depending on
the value of a controlling expression. The selection statements are the
if
statement and the
switch
statement, which are discussed in the following sections.

7.5.1 The if Statement

The
if
statement has the following syntax:

if ( expression )

statement

elseopt

else-statementopt

The statement following the control expression is executed if the value
of the control expression is true (nonzero). An
if
statement can be written with an optional
else
clause that is executed if the control expression is false (0).

Consider the following example:

if (i < 1)
funct(i);
else
{
i = x++;
funct(i);
}

In this example, if the value of
i
is less than 1, then the statement
funct(i)
is executed and the compound statement following the keyword
else
is not executed. If the value of
i
is not less than 1, then only the compound statement following the
keyword
else
is executed.

The control expression in a selection statement is usually a logical
expression, but it can be any expression of scalar type.

When
if
statements are nested, an
else
clause matches the most recent
if
statement that does not have an
else
clause, and is in the same block. For example: