Skip to Main Content United States    
PRODUCTS SUPPORT SOLUTIONS SERVICES
COMPAQ SOFTWARE
Compaq C

Compaq C
Language Reference Manual


Previous Contents Index

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 integer 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:

  • A pointer can be converted to an integral type. A pointer occupies the same amount of storage as objects of type int or long (or their unsigned equivalents). Therefore, a pointer can be converted to any of these integer types and back again without changing its value. No scaling takes place, and the representation of the value does not change.
    Converting from a pointer to a shorter integer type is similar to converting from an unsigned long type to a shorter integer type; that is, the high-order bits of the pointer are discarded.
    Converting from a shorter integer type to a pointer is similar to the conversion from a shorter integer type to an object of unsigned long type; that is, the high-order bits of the pointer are filled with copies of the sign bit. Compaq C, with the check option enabled, issues a warning message for cast operations of this type.
  • A pointer to an object or incomplete type can be converted to a pointer to a different object or a different incomplete type. The resulting pointer might not be valid if it is improperly aligned for the type pointed to. It is guaranteed, however, that a pointer to an object of a given alignment can be converted to a pointer to an object of the same alignment or less strict alignment, and back again. The result is equal to the original pointer. (An object of character type has the least strict alignment.)
  • A pointer to a function of one type can be converted to a pointer to a function of another type and back again; the result is equal to the original pointer. If a converted pointer is used to call a function that has a type not compatible with the type of the called function, the behavior is undefined.

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.4.9 The _Pragma Operator

The _Pragma operator destringizes its string literal argument, effectively allowing #pragma directives to be produced by macro expansion. When specified using this operator, the tokens of the pragma, which appear together within a single string literal in this form, are not macro expanded, even if they have _m suffix. But macro expansion can be accomplished if desired by using the stringization operator ( # ) to form the string (see Section 8.1.3).

The _Pragma operator has the following syntax:

_Pragma ( string-literal )

A _Pragma operator expression is processed as follows: The string literal is destringized by deleting the L prefix, if present, deleting the leading and trailing double-quotes, replacing each escape sequence \" by a double-quote, and replacing each escape sequence \\ by a single backslash.

The resulting sequence of characters is processed through translation phase 3 to produce preprocessing tokens that are executed as if they were the pp-tokens in a pragma directive. The original four preprocessing tokens in the unary operator expression are removed.

Example: A directive of the form:


#pragma listing on "..\listing.dir" 

can also be expressed as:


_Pragma ( "listing on \"..\\listing.dir\"" ) 

The latter form is processed in the same way, whether it appears literally as shown, or results from macro replacement, as in:


#define LISTING(x) PRAGMA(listing on #x) 
#define PRAGMA(x)  _Pragma(#x) 
 
LISTING ( ..\listing.dir ) 

6.5 Binary Operators

The binary operators are categorized as follows:

  • Multiplicative operators: multiplication (*), remainder (%), and
    division (/) (see Section 6.5.1)
  • Additive operators: addition (+) and subtraction (--) (see Section 6.5.2)
  • Shift operators: left shift (<<) and right shift (>>) (see Section 6.5.3)
  • Relational operators: less than (<), less than or equal to (<=), greater than (>), and greater than or equal to (>=) (see Section 6.5.4)
  • Equality operators: equality ( == ) and inequality ( != ) (see Section 6.5.5)
  • Bitwise operators: AND (&), OR ( | ), and XOR (^) (see Section 6.5.6)
  • Logical operators: AND (&&) and OR ( || ) (see Section 6.5.7)

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:

  • Integer overflow occurs
  • Division by zero is attempted
  • Remainder by zero is attempted


Previous Next Contents Index
Buy Online or Call 1.800.888.0220      privacy statement and legal notices 
STORES CONTACT US SEARCH PRODUCTS SOLUTIONS OPTIONS DEVELOPERS