Tru64 UNIX
Compaq C Language Reference Manual


Previous Contents Index

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:

6.4.7 The sizeof Operator

Consider the syntax of the following expressions:

sizeof expression


sizeof ( type-name )

type-name cannot be an incomplete type, function type, or a bit field. The sizeof operator produces a compile-time integer constant value. expression is inspected only to deduce its type; it is not fully evaluated. For example, sizeof(i++) is equivalent to sizeof(i) .

The result of the sizeof operation is the size, in bytes, of the operand. In the first case, the result of sizeof is the size determined by the type of the expression. In the second case, the result is the size of an object of the named type. The expression should be enclosed in parentheses if it contains operators, because the precedence of sizeof is higher than that of most operators.

The syntax of type-name is the same as that for the cast operator. For example:


int  x; 
x = sizeof(char *);  /* assigns the size of a character pointer to x */ 

The type of the sizeof operator's result, size_t , is an unsigned integer type. In Compaq C, size_t is unsigned int .

6.4.8 The __typeof__ Operator

The __typeof__ operator is another way to refer to the type of an expression. This feature is provided for compatiblity with the gcc compiler.

The syntax of this operator keyword looks like sizeof , but the construct acts semantically like a type-name defined with typedef .

__typeof__ ( expression )


__typeof__ ( type-name )

There are two ways of writing the argument to __typeof__ : with an expression or with a type.

The following is an example with an expression. This example assumes that x is an array of int s; the type described is int :


__typeof__(x[0](1)) 

The following is an example with a type-name as the argument. The type described is that of pointers to int :


__typeof__(int *) 

A __typeof__ construct can be used anywhere a typedef name can be used. For example, you can use it in a declaration, in a cast, or inside a sizeof or __typeof__ operator:


__typeof__(*x) y;     // Declares y with the type of what x points to. 
 
__typeof__(*x) y[4];  // Declares y as an array of such values. 
 
__typeof__(__typeof__(char *)[4]) y;  // Declares y as an array of 
                                      // pointers to characters: 
 

The last example (the nested __typeof__ operators) is equivalent to the following traditional C declaration:


char *y[4]; 

To see the meaning of the declaration using __typeof__ , and why it might be a useful way to write it that way, let's rewrite it with these macros:


#define pointer(T)  __typeof__(T *) 
#define array(T, N) __typeof__(T [N]) 

Now the declaration can be rewritten this way:


array (pointer (char), 4) y; 

Thus, array (pointer (char), 4) is the type of arrays of 4 pointers to char .

6.5 Binary Operators

The binary operators are categorized as follows:

The following sections describe these binary operators.

6.5.1 Multiplicative Operators

The multiplicative operators are *, /, and %. Operands must have arithmetic type. Operands are converted, if necessary, according to the usual arithmetic conversion rules (see Section 6.11.1).

The * operator performs multiplication.

The / operator performs division. When integers are divided, truncation is toward zero. If either operand is negative, the result is truncated toward zero (the largest integer of lesser magnitude than the algebraic quotient).

The % operator divides the first operand by the second and yields the remainder. Both operands must be integral. When both operands are unsigned or positive, the result is positive. If either operand is negative, the sign of the result is the same as the sign of the left operand.

The following statement is true if b is not zero:


(a/b)*b + a%b == a; 

The Compaq C compiler, with the check option enabled, issues warnings for these undefined behaviors:

6.5.2 Additive Operators

The additive operators + and -- perform addition and subtraction. 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 Compaq 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:

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).


Previous Next Contents Index