The precedence of expression operators is indicated by their syntax in this chapter; it usually follows the order of the major subsections, with earlier subsections having higher precedence. For example, since the multiplication operator * can have a unary-expression (which is a cast-expression) as well as an operand, the order of evaluation of the expression
~ i * z |
gives ~ higher precedence than * and can be written
( ~ i ) * z |
The text indicates this precedence by placing unary-expressions in “Unary Operators”, and multiplicative-expressions in “Multiplicative Operators”. This syntax–subsection correlation is violated in a few cases. For example, cast-expressions can be operands in unary-expressions, in which case the cast-expression has higher precedence. See “Cast Operators” and “Unary Operators” for more information.
Within each subsection, the operators have the same precedence. All operators group left to right, unless otherwise indicated in their discussion. Table 7-1 shows operators and indicates the priority ranking and grouping of each.
Table 7-1. Operator Precedence and Associativity
Operator (from high to low priority) | Grouping |
|---|---|
() [] -> . | L-R |
! ~ ++ -- - (type) * & sizeof (all unary) | R-L |
* / % | L-R |
+ - | L-R |
<< >> | L-R |
< <= > >= | L-R |
== != | L-R |
& | L-R |
^ | L-R |
| | L-R |
&& | L-R |
|| | L-R |
? : | L-R |
= += -= *= /= %= ^= &= |= | R-L |
, | L-R |
The order of evaluation of expressions, as well as the order in which side-effects take place, is unspecified, except as indicated by the syntax, or specified explicitly in this chapter. The compiler can arbitrarily rearrange expressions involving a commutative and associative operator (*, +, &, |, ^).
Integer divide-by-zero results in a trap. Other integer exception conditions are ignored. Silicon Graphics floating point conforms to the IEEE standard. Floating point exceptions are ignored by default, yielding the default IEEE results of infinity for divide-by-zero and overflow, not-a-number for invalid operations, and zero for underflow. You can gain control over these exceptions and their results most easily by using the Silicon Graphics IEEE floating point exception handler package (see handle_sigfpes(3c)). You can also control these exceptions by implementing your own handler and appropriately initializing the floating point unit (see fpc(3c)).
An identifier is a primary-expression, provided it has been declared as referring to an object, in which case it is an lvalue; or a function, in which case it is a function designator. Lvalues and function designators are discussed in “Conversion of lvalues and Function Designators”.
| primary-expression: | identifier constant string literal (expression) |
A constant is a primary-expression. Its type is determined by its form and value, as described in “Constants”.
A string literal is a primary-expression. Its type is array of char, subject to modification, as described in “Conversions of Characters and Integers”.
A parenthesized expression is a primary-expression whose type and value are identical to those of the unparenthesized expression. The presence of parentheses does not affect whether the expression is an lvalue, rvalue, or function designator. For information on expressions, see “Constant Expressions”.
Postfix expressions involving ., –>, subscripting, and function calls group left to right.
| postfix-expression:, argument-expression-list: | primary-expression postfix-expression [expression] postfix-expression (argument-expression-list opt) postfix-expression . identifier postfix-expression –> identifier postfix-expression ++ postfix-expression – – argument-expression argument-expression-list, argument-expression |
A postfix-expression followed by an expression in square brackets is a subscript. Usually, the postfix-expression has type pointer to <type>, the expression within the square brackets has type int, and the type of the result is <type>. However, it is equally valid if the types of the postfix-expression and the expression in brackets are reversed. This is because the expression postfix
E1[E2]
is identical (by definition) to
*((E1)+(E2))
Since + is commutative, E1 and E2 can be interchanged.
You can find further information on this notation in the discussions on identifiers, and in the discussion of the operators * (in “Unary Operators”) and + (in “Additive Operators”).
The syntax of postfix-expressions that are function calls is
postfix-expression (argument-expression-list opt)
argument-expression-list: argument-expression argument-expression-list, argument-expression |
A postfix–expression followed by parentheses containing a possibly empty, comma-separated list of expressions (which constitute the actual arguments to the function) denotes a function call. The postfix–expression must be of type function returning <type>, and the result of the function call is of type <type>, and is not an lvalue. If the postfix–expression denoting the called function consists solely of a previously unseen identifier foo, the call produces an implicit declaration as if, in the innermost block containing the call, the declaration had appeared:
extern int foo(); |
If a corresponding function prototype that specifies a type for the argument being evaluated is in force, an attempt is made to convert the argument to that type. If the number of arguments does not agree with the number of parameters specified in the prototype, the behavior is undefined. If the type returned by the function as specified in the prototype does not agree with the type derived from the postfix-expression denoting the called function, the behavior is undefined. Such a scenario may occur for an external function declared with conflicting prototypes in different files. If no corresponding prototype is in scope or the argument is in the variable argument section of a prototype that ends in ellipses (…), the argument is converted according to the following default argument promotions:
Type float is converted to double.
Array and function names are converted to corresponding pointers.
When using traditional C:
types unsigned short and unsigned char are converted to unsigned int.
types signed short and signed char are converted to signed int.
When using ANSI C:
types short and char, whether signed or unsigned, are converted to int.
In preparing for the call to a function, a copy is made of each actual argument. Thus, all argument passing in C is strictly by value. A function can change the values of its parameters, but these changes cannot affect the values of the actual arguments. It is possible to pass a pointer on the understanding that the function can change the value of the object to which the pointer points. (Arguments that are array names can be changed as well, since these arguments are converted to pointer expressions.) Since the order of evaluation of arguments is unspecified, side effects may be delayed until the next sequence point, which occurs at the point of the actual call—after all arguments have been evaluated. (For example, the incrementation of foo, which is a side-effect of the evaluation of an argument foo++, may be delayed.) Recursive calls to any function are permitted.
Silicon Graphics recommends consistent use of prototypes for function declarations and definitions, as it is extremely dangerous to mix prototyped and nonprototyped function declarations/definitions. Never call functions before you declare them (although the language allows this). It results in an implicit nonprototyped declaration that may be incompatible with the function definition.
A postfix-expression followed by a dot followed by an identifier denotes a structure or union reference.
| postfix-expression . identifier |
The postfix-expression must be a structure or a union, and the identifier must name a member of the structure or union. The value is the named member of the structure or union, and it is an lvalue if the first expression is an lvalue. The result has the type of the indicated member and the qualifiers of the structure or union.
A postfix-expression followed by an arrow (built from – and > ) followed by an identifier is an indirect structure or union reference.
| postfix-expression –> identifier |
The postfix-expression must be a pointer to a structure or a union, and the identifier must name a member of that structure or union. The result is an lvalue referring to the named member of the structure or union to which the postfix-expression points. The result has the type of the selected member, and the qualifiers of the structure or union to which the postfix-expression points. Thus the expression
E1–>MOS
is the same as
(*E1).MOS
Structures and unions are discussed in “Structure and Union Declarations”.
The syntax of postfix ++ and postfix -- is:
| postfix-expression ++ | ||
| postfix-expression – – |
When postfix ++ is applied to a modifiable lvalue, the result is the value of the object referred to by the lvalue. After the result is noted, the object is incremented as if the constant 1 (one) were added to it. See the discussions in “Additive Operators” and “Assignment Operators” for information on conversions. The type of the result is the same as the type of the lvalue expression. The result is not an lvalue.
When postfix – – is applied to a modifiable lvalue, the result is the value of the object referred to by the lvalue. After the result is noted, the object is decremented as if the constant 1 (one) were subtracted from it. See the discussions in “Additive Operators” and “Assignment Operators” for information on conversions. The type of the result is the same as the type of the lvalue expression. The result is not an lvalue.
For both postfix ++ and – – operators, updating the stored value of the operand may be delayed until the next sequence point.
Expressions with unary operators group from right to left.
| unary-expression:, unary-operator: one of | postfix-expression ++ unary-expression – – unary-expression unary-operator cast-expression sizeof unary-expression sizeof (type-name) * & – ! ~ + |
Except as noted, the operand of a unary-operator must have arithmetic type.
The unary * operator means “indirection”; the cast-expression must be a pointer, and the result is either an lvalue referring to the object to which the expression points, or a function designator. If the type of the expression is pointer to <type>, the type of the result is <type>.
The operand of the unary & operator can be either a function designator or an lvalue that designates an object. If it is an lvalue, the object it designates cannot be a bitfield, and it cannot be declared with the storage-class register. The result of the unary & operator is a pointer to the object or function referred to by the lvalue or function designator. If the type of the lvalue is <type>, the type of the result is pointer to <type>.
The result of the unary – operator is the negative of its operand. The integral promotions are performed on the operand, and the result has the promoted type and the value of the negative of the operand. Negation of unsigned quantities is analogous to subtracting the value from 2n, where n is the number of bits in the promoted type.
The unary + operator exists only in ANSI C. The integral promotions are used to convert the operand. The result has the promoted type and the value of the operand.
The result of the logical negation operator ! is 1 if the value of its operand is zero, and 0 if the value of its operand is nonzero. The type of the result is int. The logical negation operator is applicable to any arithmetic type and to pointers.
The ~ operator yields the one's complement of its operand. The usual arithmetic conversions are performed. The type of the operand must be integral.
The prefix operators ++ and – – increment and decrement their operands.
| ++ unary-expression | ||
| – – unary-expression |
The object referred to by the modifiable lvalue operand of prefix ++ is incremented. The value is the new value of the operand but is not an lvalue. The expression ++x is equivalent to x += 1. See the discussions in “Additive Operators” and “Assignment Operators” for information on conversions.
The prefix – – decrements its lvalue operand in the same manner as prefix ++ increments it.
The sizeof operator yields the size in bytes of its operand. The size of a char is 1 (one). Its major use is in communication with routines like storage allocators and I/O systems.
| sizeof unary-expression | ||
| sizeof (type-name) |
The operand of sizeof can not have function or incomplete type, or be an lvalue that denotes a bitfield. It can be an object or a parenthesized type name. In traditional C, the type of the result is unsigned. In ANSI C, the type of the result is size_t, which is defined in <stddef.h> as unsigned int (in 32-bit mode) or as unsigned long (in 64-bit mode). The result is a constant and can be used anywhere a constant is required.
When applied to an array, sizeof returns the total number of bytes in the array. The size is determined from the declaration of the object in the unary-expression. The sizeof operator can also be applied to a parenthesized type-name. In that case it yields the size in bytes of an object of the indicated type.
When sizeof is applied to an aggregate, the result includes space used for padding, if any.
A cast-expression preceded by a parenthesized type-name causes of the value the expression to convert to the indicated type. This construction is called a cast. Type names are discussed in “Type Names”.
| cast-expression: | unary-expression (type-name) cast-expression |
The type-name specifies scalar type or void, and the operand has scalar type. Since a cast does not yield an lvalue, the effect of qualifiers attached to the type name is inconsequential.
When an arithmetic value is cast to a pointer, and vice versa, the appropriate number of bits are simply copied unchanged from one type of value to the other. Be aware of the possible truncation of pointer values in 64-bit mode compilation, when a pointer value is converted to an (unsigned) int.
The multiplicative operators *, /, and % group from left to right. The usual arithmetic conversions are performed.
| multiplicative expression: | cast-expression multiplicative-expression * cast-expression multiplicative-expression / cast-expression multiplicative-expression % cast-expression |
Operands of * and / must have arithmetic type. Operands of % must have integral type.
The binary * operator indicates multiplication, and its result is the product of the operands.
The binary / operator indicates division of the first operator (dividend) by the second (divisor). If the operands are integral and the value of the divisor is 0, SIGTRAP is signalled. Integral division results in the integer quotient whose magnitude is less than or equal to that of the true quotient, and with the same sign.
The binary % operator yields the remainder from the division of the first expression (dividend) by the second (divisor). The operands must be integral. The remainder has the same sign as the dividend, so that the equality is true when the divisor is nonzero:
(dividend / divisor) * divisor + dividend % divisor == dividend |
If the value of the divisor is 0, SIGTRAP is signalled.
The additive operators + and – group from left to right. The usual arithmetic conversions are performed.
| additive-expression: | multiplicative-expression additive-expression + multiplicative-expression additive-expression – multiplicative-expression |
In addition to arithmetic types, the following type combinations are acceptable for additive-expressions:
For addition, one operand is a pointer to an object type and the other operand is an integral type.
For subtraction:
Both operands are pointers to qualified or unqualified versions of compatible object types.
The left operand is a pointer to an object type, and the right operand has integral type.
The result of the + operator is the sum of the operands. The result of the
– operator is the difference of the operands. When an operand of integral type is added to or subtracted from a pointer to an object type, the integral operand is first converted to an address offset by multiplying it by the length of the object to which the pointer points. The result is a pointer of the same type as the original pointer.
Suppose a has type array of <object>, and p has type pointer to <object> and points to the initial element of a. Then the result of p n, where n is an integral operand, is the same as &a [\xb1 n].
If two pointers to objects of the same type are subtracted, the result is converted (by division by the length of the object) to an integral quantity representing the number of objects separating them. Unless the pointers point to objects in the same array, the result is undefined. The actual type of the result is int in traditional C, and ptrdiff_t (defined in <stddef.h> as int in 32-bit mode and as long in 64-bit mode) in ANSI C.
The shift operators << and >> group from left to right. Each operand must be of an integral type. The integral promotions are performed on each operand. The type of the result is that of the promoted left operand. The result is undefined if the right operand is negative or greater than or equal to the length in bits of the promoted left operand.
| shift-expression: | additive-expression shift-expression << additive-expression shift-expression >> additive-expression |
The value of E1<<E2 is E1 (interpreted as a bit pattern) left-shifted E2 bits. Vacated bits are filled with zeros.
The value of E1>>E2 is E1 right-shifted E2 bit positions. Vacated bits are filled with zeros if E1 is unsigned, or if it's signed and its value is nonnegative. If E1 is signed and its value is negative, vacated bits are filled with ones.
The relational operators group from left to right.
| relational-expression: | shift-expression relational-expression < shift-expression relational-expression > shift-expression relational-expression <= shift-expression relational-expression >= shift-expression |
The operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) all yield a result of type int with the value 0 if the specified relation is false and 1 if it is true.
The operands must be one of the following:
both arithmetic, in which case the usual arithmetic conversions are performed on them
both pointers to qualified or unqualified versions of compatible object types
both pointers to qualified or unqualified versions of compatible incomplete types
When two pointers are compared, the result depends on the relative locations in the address space of the pointed-to objects. Pointer comparison is portable only when the pointers point to objects in the same aggregate. In particular, no correlation is guaranteed between the order in which objects are declared and their resulting addresses.
The == (equal to) and the != (not equal to) operators are exactly analogous to the relational operators except for their lower precedence. (Thus a<b == c<d is 1 whenever a<b and c<d have the same truth value.)
| equality-expression: | relational-expression equality-expression == relational-expression |
The operands must be one of the following:
both arithmetic, in which case the usual arithmetic conversions are performed on them
both pointers to qualified or unqualified versions of compatible types
a pointer to an object or incomplete type, and a pointer to qualified or unqualified void type
a pointer and a null pointer constant
The semantics detailed in “Relational Operators” apply if the operands have types suitable for those operators. Combinations of other operands have the behavior detailed below:
Two null pointers to object or incomplete types are equal. If two pointers to such types are equal, they either are null, point to the same object, or point to one object beyond the end of an array of such objects.
Two pointers to the same function are equal, as are two null function pointers. Two function pointers that are equal are either both null or both point to the same function.
Each operand must have integral type. The usual arithmetic conversions are performed. The result is the bitwise AND function of the operands, that is, each bit in the result is 0 unless the corresponding bit in each of the two operands is 1.
| AND-expression: | equality-expression AND-expression & equality-expression |
Each operand must have integral type. The usual arithmetic conversions are performed. The result has type int, long, or long long, and the value is the bitwise exclusive OR function of the operands. That is, each bit in the result is 0 unless the corresponding bit in one of the operands is 1, and the corresponding bit in the other operand is 0.
Each operand must have integral type. The usual arithmetic conversions are performed.
| inclusive-OR-expression: | exclusive-OR-expression inclusive-OR-expression | exclusive-OR-expression |
The result has type int, long, or long long, and the value is the bitwise inclusive OR function of the operands. That is, each bit in the result is 0 unless the corresponding bit in at least one of the operands is 1.
The && operator groups left to right.
| logical-AND-expression: | inclusive-OR-expression logical-AND-expression && inclusive-OR-expression |
Each of the operands must have scalar type. The result has type int and value 1 if neither of its operands evaluates to 0. Otherwise it has value 0.
Unlike &, && guarantees left to right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to zero. There is a sequence point after the evaluation of the first operand.
The || operator groups left to right.
| logical-OR-expression: | logical-AND-expression logical-OR-expression || logical-AND-expression |
Each of the operands must have scalar type. The result has type int and value 1 if either of its operands evaluates to one. Otherwise it has value 0.
Unlike |, || guarantees left to right evaluation; moreover, the second operand is not evaluated unless the first operand evaluates to zero. A sequence point occurs after the evaluation of the first operand.
Conditional expressions group from right to left.
| conditional-expression: | logical-OR-expression logical-OR-expression ? expression : conditional-expression |
The type of the first operand must be scalar. Only certain combinations of types are allowed for the second and third operands. These combinations are listed below, along with the type of result the combination yields.
Both can be arithmetic types. In this case, the usual arithmetic conversions are performed on them to derive a common type, which is the type of the result.
Both are compatible structure or union objects. The result has that type.
Both are void. The type of the result is void.
One is a pointer, and the other a null pointer constant. The type of the result is the type of the nonconstant pointer.
One is a pointer to void, and the other is a pointer to an object or incomplete type. The second operand is converted to a pointer to void, and this is the type of the result.
Both are pointers to qualified or unqualified versions of compatible types. The result has a type compatible with each, qualified with all the qualifiers of the types pointed to by both operands.
Evaluation of the conditional operator proceeds as follows. The first expression is evaluated, after which a sequence point occurs. If the value of the first expression is nonzero, the result is the value of the second operand; otherwise it is that of the third operand. Only one of the second and third operands is evaluated.
All assignment operators group from right to left.
| assignment-expression:, assignment operator: one of | conditional-expression unary-expression assignment-operator assignment-expression = *= /= %= += -= <<= >>= &= ^= |= |
Assignment operators require a modifiable lvalue as their left operand. The type of an assignment expression is that of its unqualified left operand. The result is not an lvalue. Its value is the value stored in the left operand after the assignment, but the actual update of the stored value may be delayed until the next sequence point.
The order of evaluation of the operands is unspecified.
The operands permissible in simple assignment must obey one of the following:
Both have arithmetic type or are compatible structure or union types.
Both are pointers, and the type pointed to by the left has all of the qualifiers of the type pointed to by the right.
One is a pointer to an object or incomplete type, and the other is a pointer to void. The type pointed to by the left must have all of the qualifiers of the type pointed to by the right.
The left operand is a pointer, and the right is a null pointer constant.
In simple assignment, the value of the right operand is converted to the type of the assignment expression and replaces the value of the object referred to by the left operand. If the value being stored is accessed by another object that overlaps it, the behavior is undefined unless the overlap is exact and the types of the two objects are compatible.
For the operators += and -=, either both have arithmetic types, or the left operand is a pointer and the right is an operand integral. In the latter case, the right operand is converted as explained in “Additive Operators”. For all other operators, each operand must have arithmetic type consistent with those allowed for the corresponding binary operator.
The expression E1 op = E2 is equivalent to the expression E1 = E1 op E2, except that in the former, E1 is evaluated only once.
A pair of expressions separated by a comma is evaluated left to right, and the value of the left expression is discarded.
| expression: | assignment-expression expression, assignment-expression |
The type and value of the result are the type and value of the right operand. This operator groups left to right. In contexts where comma is given a special meaning, the comma operator as described in this section can appear only in parentheses. Two such contexts are lists of actual arguments to functions (described in “Primary Expressions”) and lists of initializers (see “Initialization”). For example, the following code has three arguments, the second of which has the value 5.
f(a, (t=3, t+2), c) |
A constant expression can be used any place a constant can be used.
| constant-expression: | conditional-expression |
It cannot contain assignment, increment, decrement, function-call, or comma operators. It must evaluate to a constant that is in the range of representable values for its type. Otherwise, the semantic rules for the evaluation of nonconstant expressions apply.
Constant expressions are separated into three classes:
An integral constant expression has integral type and is restricted to operands that are integral constants, sizeof expressions, and floating constants that are the immediate operands of integral casts.
An arithmetic constant expression has arithmetic type and is restricted to operands that are arithmetic constants, and sizeof expressions. Cast expressions in arithmetic constant expressions can convert only between arithmetic types.
An address constant is a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator. It can be created explicitly or implicitly, as long as no attempt is made to access an object value.
Either address or arithmetic constant expressions can be used in initializers. In addition, initializers can contain null pointer constants and address constants (for object types), and plus or minus integral constant expressions.