United States    
COMPAQ STORE | PRODUCTS |
SERVICES | SUPPORT | CONTACT US | SEARCH
Compaq C

Compaq C
Language Reference Manual


Previous Contents Index

6.3.2 Function Calls

Function calls have the following syntax:

function-call:

postfix-expression ( argument-expression-listopt )

argument-expression-listopt:

assignment-expression
argument-expression-listopt, assignment-expression

A function call is a postfix expression consisting of a function designator followed by parentheses. The order of evaluation of any expressions in the function parameter list is undefined, but there is a sequence point before the actual call. The parentheses can contain a list of arguments (separated by commas) or can be empty. If the function called has not been declared, it is assumed to be a function returning int .

To pass an argument that is an array or function, specify the identifier in the argument list without brackets or parentheses. The compiler passes the address of the array or function to the called routine, which means that the corresponding parameters in the called function must be declared as pointers.

In the following example, func1 is declared as a function returning double ; the number and type of the parameters are not specified:


double func1(); 

The function func1 can then be used in a function call, as follows:


result = func1(c); 
      or 
result = func1(); 

The identifier func1 can also be used in other contexts, without the parentheses. For example, as an argument to another function call:


dispatch(func1); 

In this example, the address of the function func1 is passed to the function dispatch . In general, if an identifier is declared as a "function returning..." type, it is converted to "the address of function returning..." when that identifier is passed as an argument without its parentheses; the only exception is when the function designator is the operand of the unary & operator, in which case this conversion is explicit.

Functions can also be called by dereferencing a pointer to a function. In the following example, pf is declared as a pointer to a function returning double and assigned the address of the function func1 :


double (*pf)( ); 
   .
   .
   .
pf = func1; 

The function func1 can then be called as follows:


result = (*pf)(); 

Although this function call is valid, the following form of the same function call is simpler:


result = pf(); 

In function calls, if the expression that denotes the called function has a type that does not include a prototype, the integral promotions discussed in Section 6.11.3 are performed on each applicable argument, and arguments that have type float are converted to double . These are called the default argument promotions. If the number of passed arguments does not agree with the number of parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with the types of the parameters after promotion, the behavior is undefined. If the function is defined with a type that includes a prototype, and the types of the arguments after promotion are not compatible with the types of the parameters, or if the prototype ends with an ellipsis punctuator (indicating a variable-length parameter list), the behavior is undefined.

If the expression that denotes the called function has a type that includes a prototype, the passed arguments are implicitly converted to the types of the corresponding parameters. The ellipsis punctuator in a function prototype causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments. If the function is defined with a type that is not compatible with the type pointed to by the expression that denotes the called function, the behavior is undefined.

No other conversions are implicitly performed; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a prototype.

Recursive function calls are permitted, both directly and indirectly through any chain of other functions.

6.3.3 Structure and Union References

A member of a structure or union can be referenced either directly using the dot (.) operator, or indirectly using the arrow (-->) operator.

Structure and union references (also called component selections) have the following syntax:

structure-and-union-reference:

postfix-expression . identifier
postfix-expression --> identifier

The arrow operator always produces an lvalue. The dot operator produces an lvalue if the postfix expression is an lvalue.

In a direct member selection, the first operand must designate a structure or union, and the identifier must name a declared member of that structure or union.

In an indirect member selection, the first operand must be a pointer to a structure or union, and the identifier must name a declared member of that structure or union. The arrow operator is specified with a hyphen (--) and a greater-than symbol (>) and designates a reference to the structure or union member. The expression E1-->name is, by definition, precisely the same as (*E1).name. This also implies that E2.name is the same as (&E2)-->name, if E2 is an lvalue.

A named structure member must be fully qualified; that is, it must be preceded by a list of the names of any higher-level members separated by periods, arrows, or both. The value of the expression is the named member of the structure or union, and its type is the type of that member. For more information about structures and unions, see Sections 3.4.4 and 3.4.5.

With one exception, if a member of a union is accessed after a value has been stored in a different member of that union, the result is dependent on the data types of the members referenced and their alignment within the union.

The exception exists to simplify the use of unions. If a union contains several structures that share a common initial sequence, and if the union currently contains one of these structures, you can inspect the common initial part of any of them. Two structures share a common initial sequence if corresponding members have compatible types (and for bit fields, the same width) for a sequence of one or more initial members.

6.3.4 Postfix Increment and Decrement Operators

C has two unary operators for incrementing and decrementing objects of scalar type. Postfix incrementation has the following syntax:

postfix-increment-expression:

postfix-expression ++

Postfix decrementation has the following syntax:

postfix-decrement-expression:

postfix-expression -- --

The increment operator ++ adds 1 to its operand, and the decrement operator -- subtracts 1, except when the operand is a pointer. If the operand is a pointer of type pointer to T, the pointer is incremented (or decremented) by sizeof (T). The effect is to point to the next (or previous) element within an array of objects of type T.

Both ++ and -- can be used either as prefix operators (before the operand: ++n ) or postfix operators (after the operand: n++ ). In both cases, the effect is to increment n. The expression ++n increments n before its value is used, while n++ increments n after its value is used.

Section 6.4.3 describes the prefix form of the increment and decrement operators. This section addresses the postfix form.

Consider the following expression:

lvalue++

The postfix operator ++ adds the constant 1 to the operand, modifying the operand. The value of the expression is the value of the operand incremented by 1; otherwise, the result of the expression is the old value of the operand, before it was incremented. For example:


int i, j; 
j = 5; 
j++;                   /* j = 6 (j incremented by 1) */ 
i = j++;               /* i = 6, j = 7               */ 

When using the increment and decrement operators, do not depend on the order of evaluation of expressions. Consider the following ambiguous expression:


k = x[j] + j++; 

It is unspecified whether the value of j in x[j] is evaluated before or after j is incremented. To avoid ambiguity, increment the variable in a separate statement, as in the following example:


j++; 
k = x[j] + j; 

The ++ and -- operators can also be used with floating-point objects. In this case they scale the object by 1.0.

6.4 Unary Operators

Unary expressions are formed by combining a unary operator with a single operand. All unary operators are of equal precedence and have right-to-left associativity. The unary operators are:

6.4.1 Unary Plus and Minus

Consider the following expression:

-- expression

This is the negative of the operand. The operand must have an arithmetic type, and integral promotion is applied. The additive inverse of an unsigned quantity is computed by subtracting the quantity from the largest value of the unsigned type plus one.

The unary plus operator returns the value of an expression:

+ expression

Neither the unary plus nor unary minus operators produce lvalues.

6.4.2 Logical Negation

Consider the following expression:

! expression

The result is the logical (Boolean) negation of the expression. If the value of the expression is 0, the negated result is 1; if the value of the expression is not 0, the negated result is 0. The type of the result is int . The expression must have a scalar type.

6.4.3 Prefix Increment and Decrement Operators

C has two unary operators for incrementing and decrementing scalar objects. The increment operator ++ adds 1 to its operand; the decrement operator -- subtracts 1. Both ++ and -- can be used either as prefix operators (before the variable: ++n ) or postfix operators (after the variable: n++ ). In both cases, the effect is to increment n . The expression ++n increments n before its value is used, while n++ increments n after its value is used.

Section 6.3.4 describes the postfix increment and decrement operators. This section describes the prefix form.

Consider the following expression:

++modifiable lvalue

After evaluating this expression, the result is the incremented rvalue, not the corresponding lvalue. For this reason, expressions that use the increment and decrement operators in this manner cannot appear by themselves on the left side of an assignment expression where an lvalue is needed.

If declared as an integer or floating-point number, the operand is increased or decreased by 1 (or 1.0). The results of the following C statements are equivalent:


i = i + 1; 
i++; 
++i; 
i += 1; 

The following example shows the difference between the postfix and prefix forms of the increment operator:


int i, j; 
j = 5; 
i = ++j;                      /*  i = 6, j = 6  */ 
i = j++;                      /*  i = 6, j = 7  */ 

If the operand is a pointer, the address is incremented by the size of the addressed object as determined by its data type, not by the integer value 1. For example:


char *cp; 
int  *ip; 
++cp;             /* Incremented by sizeof(char) */ 
++ip;             /* Incremented by sizeof(int)  */     

Consider the following expression:

-- -- modifiable lvalue

The prefix operator -- is similar to the prefix operator ++ except that the value of the operand is decremented.

When using the increment and decrement operators, do not depend on the order of evaluation of expressions. Consider the following ambiguous expression:


k = x[j] + ++j; 

It is unspecified whether the value of j in x[j] is evaluated before or after j is incremented. To avoid ambiguity, increment the variable in a separate statement, as in the following example:


++j; 
k = x[j] + j; 

6.4.4 Address Operator and Indirection

Consider the following expression:

&lvalue

This expression results in the address of the lvalue. The lvalue can be a function designator or any lvalue that designates an object, including an unqualified array identifier. The lvalue cannot be a register variable or a bit field.

Consider the following expression:

*pointer

When an expression resolves to an address, the value stored at that address can be accessed by using the dereferencing operator (*).

If the operand of * is a function name or function pointer, then the result is a function designator. If the operand of * is a pointer to an object, then the result is an lvalue designating the object. If an invalid value (0, for example) is assigned to the pointer, then the * operation is undefined.

The dereferencing operator * always produces an lvalue. The address operator & never produces an lvalue.

6.4.5 Bitwise Negation

Consider the following expression:

~ expression

The result is the bitwise negation (one's complement) of the evaluated expression. Each 1-bit is converted into a 0-bit and vice versa. The expression must have an integer type. The compiler performs the usual arithmetic conversions (see Section 6.11.1).

6.4.6 The Cast Operator

The cast operator forces the conversion of its scalar operand to a specified scalar data type, or to void . The operator consists of a type-name, in parentheses, that precedes an expression, as follows:

( type-name ) expression

The value of the expression is converted to the named data type, as if the expression were assigned to a variable of that type. The expression's type and value are not themselves changed; the value is converted to the cast type for the duration of the cast operation. The type-name has the following syntax:

type-name:

type-specifier abstract-declarator

In simple cases, type-specifier is the keyword for a data type, such as char or double , and abstract-declarator is empty. For example:


(int)x; 

The type-specifier can also be an enum specifier, or a typedef name. The type-specifier can be a structure or union only if the abstract-declarator is a pointer. That is, the type-name can be a pointer to a structure or union, but cannot be a structure or union because structures and unions are not scalar types. For example:


(struct abc *)x   /* allowed     */ 
 
(struct abc)x     /* not allowed */ 

The abstract-declarator in a cast operation is a declarator without an identifier. Abstract declarators have the following syntax:

abstract-declarator:

empty
abstract-declarator
* abstract-declarator
abstract-declarator ( )
abstract-declarator [ constant-expression ]

The abstract-declarator cannot be empty in the following form:

(abstract-declarator)

Abstract declarators can include the brackets and parentheses that indicate arrays and functions. However, cast operations cannot force the conversion of any expression to an array, function, structure, or union. The brackets and parentheses are used in operations such as the following example, which casts the identifier P1 to pointer to array of int:


(int (*)[10]) P1; 

This kind of cast operation does not change the contents of P1 ; it only causes the compiler to treat the value of P1 as a pointer to such an array. For example, casting pointers this way can change the scaling that occurs when an integer is added to a pointer:


int *ip; 
((char *)ip) + 1;   /* Increments by 1 not by 4 */ 

Cast operators can be used in the following conversions that involve pointers:


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement