[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

6.5.2 Additive Operators

The additive operators + and - perform addition and subtraction, respectively. Operands are converted, if necessary, according to the usual arithmetic conversion rules (see Section 6.11.1).

When two enum constants or variables are added or subtracted, the type of the result is int .

When an integer is added to or subtracted from a pointer expression, the integer is scaled by the size of the object being pointed to. The result has the pointer's type. For example:


int arr[10]; 
int *p = arr; 
p = p + 1;  /* Increments by sizeof(int) */ 

An array pointer can be decremented by subtracting an integral value from a pointer or address; the same conversions apply as for addition. Pointer arithmetic also applies one element beyond the end of the array. For example, the following code works because the pointer arithmetic is limited to the elements of the array and to only one element beyond:


int i = 0; 
int x[5] = {0,1,2,3,4}; 
int y[5]; 
int *ptr = x; 
while (&y[i] != (ptr + 5)) { /*  ptr + 5 marks one beyond the end of the array */ 
  y[i] = x[i]; 
  i++; 
} 

When two pointers to elements of the same array are subtracted, the result (calculated by dividing the difference between the two addresses by the length of one element) is of type ptrdiff_t , which in HP C is int , and represents the number of elements between the two addressed elements. If the two elements are not in the same array, the result of this operation is undefined.

6.5.3 Shift Operators

The shift operators << and >> shift their left operand to the left or to the right, respectively, by the number of bits specified by the right operand. Both operands must be integral. The compiler performs integral promotions on each of the operands (see Section 6.11.1.1). The type of the result is the type of the promoted left operand. Consider the following expression:


E1 << E2

The result is the value of expression E1 shifted to the left by E2 bits. Bits shifted off the end are lost. Vacated bits are filled with zeros. The effect of shifting left is to multiply the left operand by 2 for each bit shifted. In the following example, the value of i is 100:


int n = 25; 
int m = 2; 
int i; 
 
i = n << m; 

Consider the following expression:


E1 >> E2

The result is the value of expression E1 shifted to the right by E2 bits. Bits shifted off the end are lost. If E1 is unsigned or if E1 has a signed type but nonnegative value, vacated bits are filled with zeros. If E1 has a signed type and negative value, vacated bits are filled with ones.

The result of the shift operation is undefined if the right operand is negative or if its value is greater than the number of bits in an int .

For a nonnegative left operand, the effect of shifting right is to divide the left operand by 2 for each bit shifted. In the following example, the value of i is 12:


int n = 100; 
int m = 3; 
int i; 
 
i = n >> m; 

6.5.4 Relational Operators

The relational operators compare two operands and produce a result of type int . The result is 0 if the relation is false, and 1 if it is true. The operators are: less than (<), greater than (>), less than or equal (<=), and greater than or equal (>=). Both operands must have an arithmetic type or must be pointers to compatible types. The compiler performs the necessary arithmetic conversions before the comparison (see Section 6.11.1).

When two pointers are compared, the result depends on the relative locations of the two addressed objects. Pointers to objects at lower addresses are less than pointers to objects at higher addresses. If two addresses indicate elements in the same array, the address of an element with a lower subscript is less than the address of an element with a higher subscript.

The relational operators associate from left to right. Therefore, the following statement relates a to b , and if a is less than b , the result is 1 (true). If a is greater than or equal to b , the result is 0 (false). Then, 0 or 1 is compared with c for the expression result. This statement does not determine "if b is between a and c ".


if ( a < b < c ) 
    statement; 

To check if b is between a and c , use the following code:


if ( a < b && b < c ) 
    statement; 

6.5.5 Equality Operators

The equality operators, equal ( == ) and not-equal (!=), produce a result of type int , so that the result of the following statement is 1 if both operands have the same value, and 0 if they do not:


a == b 

Operands must have one of the following type combinations:

  • Both operands have an arithmetic type.
  • Both operands are pointers to qualified or unqualified versions of compatible types.
  • One operand is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void .
  • One operand is a pointer and the other is a null pointer constant.

Operands are converted, if necessary, according to the usual arithmetic conversion rules (see Section 6.11.1).

Two pointers or addresses are equal if they identify the same storage location.

Note

Although different symbols are used for assignment (=) and equality ( == ), C allows either operator in all contexts, so be careful not to confuse them. Consider the following example:


if ( x = 1 ) 
    statement_1; 
else 
    statement_2; 


In this example, statement_1 always executes, because the result of the assignment x = 1 is equivalent to the value of x , which equals 1 (or true).

6.5.6 Bitwise Operators

The bitwise operators require integral operands. The usual arithmetic conversions are performed (see Section 6.11.1). The result of the expression is the bitwise AND (&), inclusive OR ( | ), or exclusive OR (^), of the two operands. The order of evaluation of their operands is not guaranteed.

The operands are evaluated bit by bit. The result of the & operator is 0 if one bit value is 0 and the other is 1, or if both bit values are 0. The result is 1 if both bit values are 1.

The result of the | operator is 0 if both bit values are 0. The result for each bit is 1 if either bit value is 1, or both bit values are 1.

The result of the ^ operator is 0 if both bit values are 0, or if both bit values are 1. The result for each bit is 1 if either bit value is 1 and the other is 0.

6.5.7 Logical Operators

The logical operators are AND (&&) and OR ( || ). These operators guarantee left-to-right evaluation. The result of the expression (of type int ) is either 0 (false) or 1 (true). The operands need not have the same type, but both types must be scalar. If the compiler can make an evaluation by examining only the left operand, the right operand is not evaluated. Consider the following expression:


E1 && E2

The result of this expression is 1 if both operands are nonzero, or 0 if one operand is 0. If expression E1 is 0, expression E2 is not evaluated because the result is the same regardless of E2's value.

Similarly, the following expression is 1 if either operand is nonzero, and 0 otherwise. If expression E1 is nonzero, expression E2 is not evaluated, because the result is the same regardless of E2's value.


E1 || E2

6.6 Conditional Operator

The conditional operator (?:) takes three operands. It tests the result of the first operand and then evaluates one of the other two operands based on the result of the first. Consider the following example:


E1  ?  E2  :  E3

If expression E1 is nonzero (true), then E2 is evaluated, and that is the value of the conditional expression. If E1 is 0 (false), E3 is evaluated, and that is the value of the conditional expression. Conditional expressions associate from right to left. In the following example, the conditional operator is used to get the minimum of x and y :


a = (x < y) ? x : y;     /* a = min(x, y)  */ 

There is a sequence point after the first expression (E1). The following example's result is predictable, and is not subject to unplanned side effects:


i++ > j ? y[i] : x[i]; 

The conditional operator does not produce an lvalue. Therefore, a statement such as a ? x : y = 10 is not valid.

The following restrictions apply:

  • The first operand must have a scalar type.
  • One of the following must hold for the second and third operands:
    • Both operands have an arithmetic type (the usual arithmetic conversions are performed to bring the second and third operands to a common type). The result has that type.
    • Both operands have compatible structure or union types.
    • Both operands have a type of void .
    • Both operands are pointers to qualified or unqualified versions of compatible types. The result has the composite type.
    • One operand is a pointer and the other is a null pointer constant. The result has the type of the pointer that is not a null pointer constant.
    • One operand is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void . The result has the type pointer to void.

6.7 Assignment Operators

There are several assignment operators. Assignments result in the value of the target variable after the assignment. They can be used as subexpressions in larger expressions. Assignment operators do not produce lvalues.

Assignment expressions have two operands: a modifiable lvalue on the left and an expression on the right. A simple assignment consists of the equal sign (=) between two operands:


E1 = E2; 

The value of expression E2 is assigned to E1. The type is the type of E1, and the result is the value of E1 after completion of the operation.

A compound assignment consists of two operands, one on either side of the equal sign (=), in combination with another binary operator. For example:


E1 += E2; 

This is equivalent to the following simple assignment (except that in the compound assignment E1 is evaluated once, while in the simple assignment E1 is evaluated twice):


E1 = E1 + E2; 

In the following example, the following assignments are equivalent:


a *= b + 1; 
 
a = a * (b + 1);         

In another example, the following expression adds 100 to the contents of number[1] :


number[1] += 100; 

The result of this expression is the result after the addition and has the same type as number[1] .

If both assignment operands are arithmetic, the right operand is converted to the type of the left before the assignment (see Section 6.11.1).

The assignment operator (=) can be used to assign values to structures and unions. In the VAX C compatibility mode of HP C, one structure can be assigned to another as long as the structures are defined to be the same size, in bytes. In ANSI mode, the structure values must also have the same type. With all compound assignment operators, all right operands and all left operands must be either pointers or evaluate to arithmetic values. If the operator is -= or += , the left operand can be a pointer, and the right operand (which must be integral) is converted in the same manner as the right operand in the binary plus (+) and minus ( - ) operations.

Do not reverse the characters that comprise a compound assignment operator, as in the following example:


E1 =+ E2; 

This is an obsolete form that is no longer supported, but it will pass through the compiler undetected. (It is interpreted as an assignment operator followed by the unary plus operator).

6.8 Comma Operator

When two or more expressions are separated by the comma operator, they evaluate from left to right. The result has the type and value of the rightmost expression (although side effects of the other expressions, if any, do take place). The result is not an lvalue. In the following example, the value 1 is assigned to R , and the value 2 is assigned to T :


R = T = 1,   T += 2,   T -= 1; 

Side effects for each expression are completed before the next expression is evaluated.

A comma expression must be enclosed with parentheses if it appears where commas have some other meaning, as in argument and initializing lists. Consider the following expression:


f(a, (t=3,t+2), c) 

This example calls the function f with the arguments a , 5 , and c . In addition, variable t is assigned the value 3 .

6.9 Constant Expressions

A constant expression is an expression that contains only constants. A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur. In the following example, limit+1 is a constant expression, and is evaluated at compile time:


#define limit 500 
char x[limit+1] 

A constant expression cannot contain assignment, increment, decrement, function-call, or comma operators, except when they are within the operand of a sizeof operator. Each constant expression must evaluate to a constant that is in the range of representable values for its type.

There are several contexts in which C requires an expression that must evaluate to a constant:

  • The size of a bit field
  • The value of an enumeration constant
  • The size of an array (and the second and subsequent dimensions in all array declarations)
  • The value of a case label
  • An integral constant expression used in conditional-inclusion preprocessing directives
  • The initializer list for an object with static storage duration

6.9.1 Integral Constant Expressions

An integral constant expression has an integral type and contains only operands that are integer constants, enumeration constants, character constants, sizeof expressions whose operand does not have variable-length array type or a parenthesized name of such a type, or floating constants that are the immediate operands of casts. Cast operands in an integral constant expression only convert arithmetic types to integral types, except as part of an operand to the sizeof operator.

C allows more latitude for constant expressions in initializers. Such a constant expression can evaluate to one of the following:

  • An arithmetic constant expression
  • A null pointer constant
  • An address constant
  • An address constant for an object type plus or minus an integral constant expression

6.9.2 Arithmetic Constant Expressions

An arithmetic constant expression has an arithmetic type and contains only operands that are integer constants, floating constants, enumeration constants, character constants, or sizeof expressions whose operand does not have variable-length array type or a parenthesized name of such a type. Cast operators in an arithmetic constant expression only convert arithmetic types to arithmetic types, except as part of an operand to the sizeof operator.

6.9.3 Address Constants

An address constant is a pointer to an lvalue designating an object of static storage duration (see Section 2.10), or to a function designator. Address constants must be created explicitly by using the unary & operator, or implicitly by using an expression of array or function type. The array subscript [] and member access operators . and ->, the address & and indirection * unary operators, and pointer casts can be used to create an address constant, but the value of an object cannot be accessed by use of these operators.

6.10 Compound Literal Expressions

A compound literal, also called a constructor expression, is a form of expression that constructs the value of an object, including objects of array, struct, or union type.

In the C89 Standard, passing a struct value to a function typically involves declaring a named object of the type, initializing its members, and passing that object to the function. With the C99 Standard, this can now be done with a single compound literal expression. (Note that compound literal expressions are not supported in the common C, VAX C, and Strict ANSI89 modes of the HP C compiler.)

A compound literal is an unnamed object specified by a syntax consisting of a parenthesized type name (the same syntax as a cast operator1) followed by a brace-enclosed list of initializers. The value of this unnamed object is given by the initializer list. The initializer list can use the designator syntax.

For example, to construct an array of 1000 int s that are all zero except for array element 9, which is to have a value of 5, you can write the following:


(int [1000]){[9] = 5}. 

A compound literal object is an lvalue. The object it designates has static storage duration if it occurs outside all function definitions. Otherwise, it has automatic storage duration associated with the nearest enclosing block.

Usage Notes

  • The type name must specify an object type or an array of unknown size.
  • An initializer cannot provide a value for an object not contained within the entire unnamed object specified by the compound literal.
  • If the compound literal occurs outside the body of a function, the initializer list must consist of constant expressions.
  • If the type name specifies an array of unknown size, the size is determined by the initializer list as specified in Section 4.7.1, and the type of the compound literal is that of the completed array type. Otherwise (when the type name specifies an object type), the type of the compound literal is that specified by the type name. In either case, the result is an lvalue.
  • All the semantic rules and constraints for initializer lists in Sections 4.2, 4.7.1, 4.8.4, 4.8.5, and 4.9 are applicable to compound literals.
  • String literals, and compound literals with const-qualified types, need not designate distinct objects. This allows implementations to share storage for string literals and constant compound literals with the same or overlapping representations.

The following examples illustrate the use of compound literals.


Examples

#1

int *p = (int []){2, 4}; 
      

This example initializes p to point to the first element of an array of two int s, the first having the value 2 and the second having the value 4. The expressions in this compound literal are required to be constant. The unnamed object has static storage duration.

#2

void f(void) 
{ 
        int *p; 
        /*...*/ 
        p = (int [2]){*p}; 
        /*...*/ 
} 
      

In this example, p is assigned the address of the first element of an array of two int s, the first having the value previously pointed to by p and the second having the value zero. The expressions in this compound literal need not be constant. The unnamed object has automatic storage duration.

#3

drawline((struct point){.x=1, .y=1}, 
        (struct point){.x=3, .y=4}); 
 
Or, if drawline instead expected pointers to struct point: 
 
drawline(&(struct point){.x=1, .y=1}, 
        &(struct point){.x=3, .y=4}); 
      

Initializers with designations can be combined with compound literals. Structure objects created using compound literals can be passed to functions without depending on member order.

#4

(const float []){1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6} 
      

A read-only compound literal can be specified through constructions like the one in this example.

#5

"/tmp/testfile" 
(char []){"/tmp/testfile"} 
(const char []){"/tmp/testfile"} 
      

The three expressions in this example have different meanings:

The first always has static storage duration and has type "array of char", but need not be modifiable.

The last two have automatic storage duration when they occur within the body of a function, and the first of these two is modifiable.

#6

(const char []){"abc"} == "abc" 
      

Like string literals, const -qualified compound literals can be placed into read-only memory and can even be shared. This example might yield 1 if the literal's storage is shared.

#7

struct int_list { int car; struct int_list *cdr; }; 
struct int_list endless_zeros = {0, &endless_zeros}; 
eval(endless_zeros); 
      

Note

1 However, this differs from a cast expression in that a cast specifies a conversion to scalar types or void only, and the result of a cast expression is not an lvalue.


Previous Next Contents Index