[an error occurred while processing this directive]

HP OpenVMS Systems

C Programming Language
Content starts here HP C

HP C
Language Reference Manual


Previous Contents Index

5.5.2 Scope and Conversions

Prototypes must be placed appropriately in each compilation unit of a program. The position of the prototype determines its scope. A function prototype, like any function declaration, is considered within the scope of a corresponding function call only if the prototype is specified within the same block as the function call, any enclosing block, or at the outermost level of the source file. The compiler checks all function definitions, declarations, and calls from the position of the prototype to the end of its scope. If you misplace the prototype so that a function definition, declaration, or call occurs outside the scope of the prototype, any calls to that function behave as if there were no prototype.

The syntax of the function prototype is designed so that you can extract the function header of each of your function definitions, add a semicolon (;), place the prototypes in a header, and include that header at the top of each compilation unit in your program. In this way, function prototypes are declared to be external, extending the scope of the prototype throughout the entire compilation unit. To use prototype checking for C library function calls, place the #include preprocessor directives for the .h files appropriate for the library functions used in the program.

It is an error if the number of arguments in a function definition, declaration, or call does not match the prototype.

If the data type of an argument in a function call does not match the corresponding type in the function prototype, the compiler tries to perform conversions. If the mismatched argument is assignment-compatible with the prototype parameter, the compiler converts the argument to the data type specified in the prototype, according to the argument conversion rules (see Section 5.6.1).

If the mismatched argument is not assignment-compatible with the prototype parameter, an error message is issued.

5.6 Parameters and Arguments

C functions exchange information by means of parameters and arguments. The term parameter refers to any declaration within the parentheses following the function name in a function declaration or definition; the term argument refers to any expression within the parentheses of a function call.

The following rules apply to parameters and arguments of C functions:

  • Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero.
  • The maximum number of arguments (and corresponding parameters) is 253 for a single function.
  • Arguments are separated by commas. However, the comma is not an operator in this context, and the arguments can be evaluated by the compiler in any order. There is, however, a sequence point before the actual call.
  • Arguments are passed by value; that is, when a function is called, the parameter receives a copy of the argument's value, not its address. This rule applies to all scalar values, structures, and unions passed as arguments.
  • Modifying a parameter does not modify the corresponding argument passed by the function call. However, because arguments can be addresses or pointers, a function can use addresses to modify the values of variables defined in the calling function.
  • In the old style, parameters that are not explicitly declared are assigned a default type of int .
  • The scope of function parameters is the function itself. Therefore, parameters of the same name in different functions are unrelated.

5.6.1 Argument Conversions

In a function call, the types of the evaluated arguments must match the types of their corresponding parameters. If they do not match, the following conversions are performed in a manner that depends on whether a prototype is in scope for the function:

  • Arguments to functions specified with prototypes are converted to the parameter types specified in the prototype, except that arguments corresponding to an ellipsis (...) are converted as if no prototype were in scope. (In this case, the rules in the following bullet apply.) For example:


    void f(char, short, float, ...); 
     
    char c1, c2; 
    short s1,s2; 
    float f1,f2; 
     
    f(c1, s1, f1, c2, s2, f2); 
    

    The arguments c1 , s1 , and f1 are passed with their respective types, while the arguments c2 , s2 , and f2 are converted to int , int , and double , respectively.
  • Arguments to functions that have no prototype in scope are not converted to the types of the parameters. Instead, the expressions in the argument list are converted according to the following rules:
    • Any arguments of type float are converted to double .
    • Any arguments of types char , unsigned char , short , or unsigned short are converted to int .
    • When compiling in common C compatibility mode, HP C converts any arguments of types unsigned char or unsigned short to unsigned int .

No other default conversions are performed on arguments. If a particular argument must be converted to match the type of the corresponding parameter, use the cast operator. For more information about the cast operator, see Section 6.4.6.

5.6.2 Function and Array Identifiers as Arguments

Function and array identifiers can be specified as arguments to a function. Function identifiers are specified without parentheses, and array identifiers are specified without brackets. When so specified, the function or array identifier is evaluated as the address of that function or array. Also, the function must be declared or defined, even if its return value is an integer. Example 5-1 shows how and when to declare functions passed as arguments, and how to pass them.

Example 5-1 Declaring Functions Passed as Arguments

(1)int x()  { return 25; }             /* Function definition and   */ 
int z[10];                          /* array defined before use  */ 
 
(2)fn(int f1(), int (*f2)(), int a1[]))   /* Function definition       */ 
{ 
    f1();                           /* Call to function f1       */ 
      . 
      . 
      . 
} 
 
void caller(void) 
{ 
(3)   int y();                         /* Function declaration      */ 
      . 
      . 
      . 
(4)   fn(x, y, z);                     /* Function call: functions  */ 
                                    /* x and y, and array z      */ 
                                    /* passed as addresses       */ 
      . 
      . 
      . 
} 
int y(void) { return 30; }          /* Function definition       */ 

Key to Example 5-1:

  1. Without being declared in a separate declaration, function x can be passed in an argument list because its definition, located before the function caller , serves as its declaration.
  2. Parameters that represent functions can be declared either as functions or as pointers to functions. Parameters that represent arrays can be declared either as arrays or as pointers to the element type of the array. For example:


    fn(int f1(), int f2(), int a1[])      /* f1, f2 declared as     */ 
    {...}                                 /* functions; a1 declared */ 
                                          /* as array of int.       */ 
     
     
    fn(int (*f1)(), int (*f2)(), int *a1) /* f1, f2 declared as     */ 
    {...}                                 /* pointers to functions; */ 
                                          /* a1 declared as pointer */ 
                                          /* to int.                */ 
    

    When such parameters are declared as functions or arrays, the compiler automatically converts the corresponding arguments to pointers.
  3. Because its function definition is located after the function caller , function y must be declared before passing it in an argument list.
  4. When passing functions as arguments, do not include parentheses. Similarly, when specifying arrays, do not include subscripts.

5.6.3 Passing Arguments to the main Function

The function called at program startup is named main . The main function can be defined with no parameters or with two parameters (for passing command-line arguments to a program when it begins executing). The two parameters are referred to here as argc and argv, though any names can be used because they are local to the function in which they are declared. A main function has the following syntax:


int main(void) {...}


int main(int argc, char *argv[ ]) {...})

argc

The number of arguments in the command line that invoked the program. The value of argc is nonnegative.

argv

Pointer to an array of character strings that contain the arguments, one per string. The value argv[argc] is a null pointer.

If the value of argc is greater than zero, the array members argv[0] through argv[argc - 1] inclusive contain pointers to strings, which are given implementation-defined values by the host environment before program startup. The intent is to supply the program with information determined before program startup from elsewhere in the host environment. If the host environment cannot supply strings with letters in both uppercase and lowercase, the host environment ensures that the strings are received in lowercase.

If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] is the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc - 1] represent the program parameters.

The parameters argc and argv, and the strings pointed to by the argv array, can be modified by the program and keep their last-stored values between program startup and program termination.

In the main function definition, parameters are optional. However, only the parameters that are defined can be accessed.

See your platform-specific HP C documentation for more information on the passing and return of arguments to the main function.


Chapter 6
Expressions and Operators

An expression is any sequence of C operators and operands that produces a value or generates a side effect. The simplest expressions are constants and variable names, which yield values directly. Other expressions combine operators and subexpressions to produce values. An expression has a type as well as a value.

Except where noted in this chapter, the order of evaluation of subexpressions, and the order in which side effects take place, is unspecified. Code that depends on such order might produce unexpected results.

The operands of expressions must have compatible type. In some instances, the compiler makes conversions to force the data types of the operands to be compatible.

The following sections discuss these topics:

6.1 Primary Expressions

Simple expressions are called primary expressions; they denote values. Primary expressions include previously declared identifiers, constants, string literals, and parenthesized expressions.

Primary expressions have the following syntax:

primary-expression:


identifier
constant
string-literal
expression

The following sections describe the primary expressions.

6.1.1 Identifiers

An identifier is a primary expression provided it is declared as designating an object or a function.

An identifier that designates an object is an lvalue if its type is arithmetic, structure, union, or pointer. The name of an array evaluates to the address of the first element of the array; an array name is an lvalue but not a modifiable lvalue.

An identifier that designates a function is called a function designator. A function designator evaluates to the address of the function.

6.1.2 Constants

A constant is a primary expression. Its type depends on its form (integer, character, floating, or enumeration); see Section 1.9. A constant is never an lvalue.

6.1.3 String Literals

A string literal is a primary expression. Its type depends on its form (character or wchar_t ); see Section 1.9. A string literal is an lvalue.

6.1.4 Parenthesized Expressions

An expression within parentheses has the same type and value as the expression without parentheses would have. Any expression can be delimited by parentheses to change the precedence of its operators.

6.2 Overview of the C Operators

Variables and constants can be used in conjunction with C operators to create more complex expressions. Table 6-1 presents the set of C operators.

Table 6-1 C Operators
Operator Example Description
() f() Function call
[] a[10] Array reference
-> s->a Structure and union member selection
. s.a Structure and union member selection
+ [unary] +a Value of a
- [unary] -a Negative of a
* [unary] *a Reference to object at address a
& [unary] &a Address of a
~ ~a One's complement of a
++ [prefix] ++a The value of a after increment
++ [postfix] a++ The value of a before increment
-- [prefix] --a The value of a after decrement
-- [postfix] a-- The value of a before decrement
sizeof sizeof (t1) Size in bytes of object with type t1
sizeof sizeof e Size in bytes of object having the type of expression e
__typeof__ __typeof__ (t1) Type of type t1
__typeof__ __typeof__ (e) Type of expression e
_Pragma _Pragma ( string-literal) destringize string-literal
+ [binary]
- [binary]
* [binary]
/
%
a + b
a - b
a * b
a / b
a % b
a plus b
a minus b
a times b
a divided by b
Remainder of a/b
>>
<<
a >> b
a << b
a, right-shifted b bits
a, left-shifted b bits
<
>
<=
>=
==
!=
a < b
a > b
a <= b
a >= b
a == b
a != b
1 if a < b; 0 otherwise
1 if a > b; 0 otherwise
1 if a <= b; 0 otherwise
1 if a >= b; 0 otherwise
1 if a equal to b; 0 otherwise
1 if a not equal to b; 0 otherwise
& [binary]
|
^
a & b
a | b
a ^ b
Bitwise AND of a and b
Bitwise OR of a and b
Bitwise XOR (exclusive OR) of a and b
&&
||
!
a && b
a || b
!a
Logical AND of a and b (yields 0 or 1)
Logical OR of a and b (yields 0 or 1)
Logical NOT of a (yields 0 or 1)
?: a ? e1 : e2 Expression e1 if a is nonzero;
Expression e2 if a is zero
=
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
,
a = b
a += b
a -= b
a *= b
a /= b
a %= b
a >>= b
a <<= b
a &= b
a |= b
a ^= b
e1,e2
a, after b is assigned to it
a plus b (assigned to a)
a minus b (assigned to a)
a times b (assigned to a)
a divided by b (assigned to a)
Remainder of a/b (assigned to a)
a, right-shifted b bits (assigned to a)
a, left-shifted b bits (assigned to a)
a AND b (assigned to a)
a OR b (assigned to a)
a XOR b (assigned to a)
e2 (e1 evaluated first)

The C operators fall into the following categories:

  • Postfix operators, which follow a single operand.
  • Unary prefix operators, which precede a single operand.
  • Binary operators, which take two operands and perform a variety of arithmetic and logical operations.
  • The conditional operator (a ternary operator), which takes three operands and evaluates either the second or third expression, depending on the evaluation of the first expression.
  • Assignment operators, which assign a value to a variable.
  • The comma operator, which guarantees left-to-right evaluation of comma-separated expressions.

Operator precedence determines the grouping of terms in an expression. This affects how an expression is evaluated. Certain operators have higher precedence than others; for example, the multiplication operator has higher precedence than the addition operator:


x = 7 + 3 * 2;        /* x is assigned 13, not 20   */ 

The previous statement is equivalent to the following:


x = 7 + ( 3 * 2 ); 

Using parenthesis in an expression alters the default precedence. For example:


x = (7 + 3) * 2;  /*  (7 + 3) is evaluated first    */ 

In an unparenthesized expression, operators of higher precedence are evaluated before those of lower precedence. Consider the following expression:


A+B*C 

The identifiers B and C are multiplied first because the multiplication operator (*) has higher precedence than the addition operator (+).


Previous Next Contents Index