|
Variable names, function names, message expressions, array names, constants, function calls, array references, and structure and union references are all considered expressions. Applying a unary operator (where appropriate) to one of these expressions is also an expression, as is combining two or more of these expressions with a binary or ternary operator. Finally, an expression enclosed within parentheses is also an expression. An expression of any type other than void that identifies a data object is called an lvalue. If it can be assigned a value, it is known as a modifiable lvalue. Modifiable lvalue expressions are required in certain places. The expression on the left side of an assignment operator must be a modifiable lvalue.The unary address operator can be applied only to a modifiable lvalue or a function name. Finally, the increment and decrement operators can be applied only to modifiable lvalues. Summary of Objective-C OperatorsTable B.4 summarizes the various operators in the Objective-C language.These operators are listed in order of decreasing precedence, and operators grouped together have the same precedence. As an example of how to use Table B.4, consider the following expression: b | c & d * e The multiplication operator has higher precedence than both the bitwise OR and bitwise AND operators because it appears above both of these in Table B.4. Similarly, the bitwise AND operator has higher precedence than the bitwise OR operator because the former appears above the latter in the table.Therefore, this expression would be evaluated as b | ( c & ( d * e ) ) Now, consider the following expression: b % c * d Table B.4 Summary of Objective-C Operators
| | Operator | Description | Associativity |
| | () | Function call | | | [] | Array element reference or message expression | | | -> | Pointer to structure member reference | Left to right | | . | Structure member reference or method call | |
| | - | Unary minus | | | + | Unary plus | | | ++ | Increment | | | -- | Decrement | | | ! | Logical negation | | | ~ | Ones complement | Right to left | | * | Pointer reference (indirection) | | | & | Address | | | sizeof | Size of an object | | | (type) | Type cast (conversion) | |
| | * | Multiplication | | | / | Division | Left to right | | % | Modulus | |
| | + | Addition | Left to right | | - | Subtraction | |
| | << | Left shift | Left to right | | >> | Right shift | | | < | Less than | |
| | <= | Less than or equal to | Left to right | | > | Greater than | | | >= | Greater than or equal to | |
| | == | Equality | Left to right | | != | Inequality | |
| | & | Bitwise AND | Left to right | | ^ | Bitwise XOR | Left to right | | | | Bitwise OR | Left to right | | && | Logical AND | Left to right | | || | Logical OR | Left to right | | ?: | Conditional | Right to left | | = *= /= %= += -= &= ^= |= <<= >>= | Assignment operators | Right to left | | , | Comma operator | Right to left |
|
Because the modulus and multiplication operators appear in the same grouping in Table B.4, they have the same precedence.The associativity listed for these operators is left to right, indicating that the expression would be evaluated as follows: ( b % c ) * d As another example, the expression ++a->b would be evaluated as ++(a->b) because the -> operator has higher precedence than the ++ operator. Finally, because the assignment operators group from right to left, the statement a = b = 0; would be evaluated as a = (b = 0); which would have the net result of setting the values of a and b to 0. In the case of the expression x[i] + ++i it is not defined whether the compiler will evaluate the left side of the plus operator or the right side first. Here, the way that it’s done affects the result because the value of i might be incremented before x[i] is evaluated. Another case in which the order of evaluation is not defined is in the expression shown here: x[i] = ++i In this situation, it is not defined whether the value of i will be incremented before or after its value is used to index into x. The order of evaluation of function and method arguments is also undefined.Therefore, in the function call f (i, ++i); or in the message expression [myFract setTo: i over: ++i]; i might be incremented first, thereby causing the same value to be sent as the two arguments to the function or method. The Objective-C language guarantees that the && and || operators will be evaluated from left to right. Furthermore, in the case of &&, it is guaranteed that the second operand will not be evaluated if the first is 0; in the case of ||, it is guaranteed that the second operand will not be evaluated if the first is nonzero.This fact is worth considering when forming expressions such as if ( dataFlag || [myData checkData] ) ... because, in this case, checkData is invoked only if the value of dataFlag is 0.As another example, if the array object a is defined to contain n elements, the statement that begins if (index >= 0 && index < n && ([a objectAtIndex: index] == 0)) ... references the element contained in the array only if index is a valid subscript into the array. Constant ExpressionsA constant expression is an expression in which each of the terms is a constant value. Constant expressions are required in the following situations: - As the value after a case in a switch statement
- For specifying the size of an array
- For assigning a value to an enumeration identifier
- For specifying the bit field size in a structure definition
- For assigning initial values to external or static variables
- For specifying initial values to global variables
- As the expression following the #if in a #if preprocessor statement
In the first four cases, the constant expression must consist of integer constants, character constants, enumeration constants, and sizeof expressions.The only operators that can be used are the arithmetic operators, bitwise operators, relational operators, conditional expression operator, and type cast operator. In the fifth and sixth cases, in addition to the rules cited earlier, the address operator can be implicitly or explicitly used. However, it can be applied only to external or static variables or functions. So, for example, the expression &x + 10 would be a valid constant expression, provided that x is an external or static variable. Furthermore, the expression &a[10] - 5 is a valid constant expression if a is an external or static array. Finally, because &a[0] is equivalent to the expression a a + sizeof (char) * 100 is also a valid constant expression. For the last situation that requires a constant expression (after the #if), the rules are the same as for the first four cases, except the sizeof operator, enumeration constants, and type cast operator cannot be used. However, the special defined operator is permitted (see the section “The #if Directive”). Arithmetic OperatorsGiven that | a, b | | are expressions of any basic data type except void; | | i, j | | are expressions of any integer data type; |
the expression -a
| | negates the value of a; | +a
| | gives the value of a; | a + b
| | adds a with b; | a - b
| | subtracts b from a; | a * b
| | multiplies a by b; | a / b
| | divides a by b; | i % j
| | gives the remainder of i divided by j. |
In each expression, the usual arithmetic conversions are performed on the operands (see the section “Conversion of Basic Data Types”). If a is unsigned, -a is calculated by first applying integral promotion to it, subtracting it from the largest value of the promoted type, and adding 1 to the result. If two integral values are divided, the result is truncated. If either operand is negative, the direction of the truncation is not defined (that is, –3 / 2 can produce –1 on some machines and –2 on others); otherwise, truncation is always toward 0 (3 / 2 always produces 1). See the section “Basic Operations with Pointers” for a summary of arithmetic operations with pointers. Logical OperatorsGiven that | a, b | | are expressions of any basic data type except void, or are both pointers; |
the expression | a && b | | has the value 1 if both a and b are nonzero and 0 otherwise (and b is evaluated only if a is nonzero); | | a || b | | has the value 1 if either a or b is nonzero and 0 otherwise (and b is evaluated only if a is 0);
| | ! a | | has the value 1 if a is 0, and 0 otherwise. |
The usual arithmetic conversions are applied to a and b (see the section “Conversion of Basic Data Types”). The type of the result in all cases is int. Relational OperatorsGiven that | a, b | | are expressions of any basic data type except void, or are both pointers;
|
the expression a < b
| | has the value 1 if a is less than b, and 0 otherwise;
| | a <= b | | has the value 1 if a is less than or equal to b, and 0 otherwise; | | a > b | | has the value 1 if a is greater than b, and 0 otherwise; | | a >= b | | has the value 1 if a is greater than or equal to b, and 0 otherwise; | | a == b | | has the value 1 if a is equal to b, and 0 otherwise; | | a != b | | has the value 1 if a is not equal to b, and 0 otherwise. |
The usual arithmetic conversions are performed on a and b (see the section “Conversion of Basic Data Types”). The first four relational tests are meaningful for pointers only if they both point into the same array or to members of the same structure or union. The type of the result in each case is int. Bitwise Operators Given that | i, j, n | | are expressions of any integer data type; | the expression | i & j | | performs a bitwise AND of i and j;
| | i | j | | performs a bitwise OR of i and j; | | i ^ j | | performs a bitwise XOR of i and j; | | ~i | | takes the ones complement of i; | | i << n | | shifts i to the left n bits; | | i >> n | | shifts i to the right n bits. | The usual arithmetic conversions are performed on the operands, except with << and >>, in which case just integral promotion is performed on each operand (see the section “Conversion of Basic Data Types”). If the shift count is negative or is greater than or equal to the number of bits contained in the object being shifted, the result of the shift is undefined. On some machines, a right shift is arithmetic (sign fill) and on others logical (zero fill). The type of the result of a shift operation is that of the promoted left operand. Increment and Decrement Operators Given that | l | | is a modifiable lvalue expression, whose type is not qualified as const; | the expression | ++l | | increments l and then uses its value as the value of the expression;
| | l++ | | uses l as the value of the expression and then increments l; | | --1 | | decrements l and then uses its value as the value of the expression; | | l-- | | uses l as the value of the expression and then decrements l. | The section “Basic Operations with Pointers” describes these operations on pointers. Assignment Operators Given that | l | | is a modifiable lvalue expression, whose type is not qualified as const; | | op | | is any operator that can be used as an assignment operator (see Table B.4);
| | a | | is an expression; | the expression | l = a | | stores the value of a into l;
| | l op= a | | applies op to l and a, storing the result into l. | In the first expression, if a is one of the basic data types (except void), it is converted to match the type of l. If l is a pointer, a must be a pointer to the same type as l, a void pointer, or the null pointer. If l is a void pointer, a can be of any pointer type.The second expression is treated as if it were written l = lop (a), except l is evaluated only once (consider x[i++] += 10). Conditional Operator Given that the expression | a ? b : c | | has as its value b if a is nonzero, and c otherwise. Only expression b or c is evaluated.
| Expressions b and c must be of the same data type. If they are not, but are both arithmetic data types, the usual arithmetic conversions are applied to make their types the name. If one is a pointer and the other is 0, the latter is taken as a null pointer of the same type as the former. If one is a pointer to void and the other is a pointer to another type, the latter is converted to be a pointer to void and is the resulting type. Type Cast Operator Given that | type | | is the name of a basic data type, an enumerated data type (preceded by the keyword enum), or a typedef-defined type, or is a derived data type;
| | a | | is an expression; | the expression | ( type ) | | converts a to the specified type. | Note that the use of a parenthesized type in a method declaration or definition is not an example of the use of the type cast operator. sizeof Operator Given that | type | | is as described previously;
| | a | | is an expression; | the expression sizeof (type) | | has as its value the number of bytes needed to contain a value of the specified type;
| | sizeof a | | has as its value the number of bytes required to hold the result of the evaluation of a. | If type is char, the result is defined to be 1. If a is the name of an array that has been imensioned (either explicitly or implicitly through initialization) and is not a formal parameter or undimensioned extern array, sizeof a gives the number of bytes required to store the elements in a. If a is the name of a class, sizeof (a) gives the size of the data structure needed to hold an instance of a. The type of the integer produced by the sizeof operator is size_t, which is defined in the standard header file <stddef.h>. If a is a variable length array, then the expression is evaluated at runtime; otherwise, it is evaluated at compile time and can be used in constant expressions (refer to the section “Constant Expressions”). Comma Operator Given that the expression | a, b | | causes a to be evaluated and then b to be evaluated. The type and value of the expression are that of b.
| Basic Operations with Arrays Given that | a | | are expressions;
| | i | | is an expression of any integer data type;
| | v | | is an expression; | the expression | a[0] | | references the first element of a;
| | a[n - 1] | | references the last element of a;
| | a[i] | | references element number i of a;
| | a[i] = v | | stores the value of v into a[i]. | In each case, the type of the result is the type of the elements contained in a. See the section “Basic Operations with Pointers” for a summary of operations with pointer and arrays. Basic Operations with Structures Note This also applies to unions.
Given that | x | | is a modifiable lvalue expression of type struct s; | | y | | is an expression of type struct s; | | m | | is the name of one of the members of the structure s; | | obj | | is any object; | | M | | is any method; | | v | | is an expression; | the expression | x | | references the entire structure and is of type struct s;
| | y.m | | references the member m of the structure y and is of the type declared for the member m; | | x.m = v | | assigns v to the member m of x and is of the type declared for the member m;
| | x = y | | assigns y to x and is of type struct s;
| f (y)
| | calls the function f, passing contents of the structure y as the argument (inside f, the formal parameter must be declared to be of type struct s);
| [obj M: y]
| | invokes the method M on the object obj, passing the contents of the structure y as the argument (inside the method, the parameter must be declared to be of type struct s); | return y;
| | returns the structure y (the return type declared for the function or method must be struct s).
| Basic Operations with Pointers Given that | x | | is an lvalue expression of type t; | | pt | | is a modifiable lvalue expression of type “pointer to t”; | | v | | is an expression; | the expression | &x | | produces a pointer to x and has type “pointer to t”;
| | pt = &x | | sets pt pointing to x and has type “pointer to t”;
| | pt = 0 | | assigns the null pointer to pt;
| | pt == 0 | | tests whether pt is null; | | *pt | | references the value pointed to by pt and has type t;
| | *pt = v | | stores the value of v into the location pointed to by pt and has type t. | Pointers to Arrays Given that | a | | is an array of elements of type t;
| | pa1 | | is a modifiable lvalue expression of type “pointer to t” that points to an element in a; | | pa2 | | is an lvalue expression of type “pointer to t” that points to an element in a, or to one past the last element in a; | v
| | is an expression;
| n
| | is an integral expression;
| the expression | a, &a, &a[0] | each produces a pointer to the first element;
| | &a[n] | produces a pointer to element number n of a and has type “pointer to t”; | | *pa1 | references the element of a that pa1 points to and has type t; | | *pa1 = v | stores the value of v into the element pointed to by pa1 and has type t; | | ++pa1 | sets pa1 pointing to the next element of a, no matter which type of elements is contained in a, and has type “pointer to t”; | | --pa1 | sets pa1 pointing to the previous element of a, no matter which type of elements is contained in a, and has type “pointer to t”; | | *++pa1 | increments pa1 and then references the value in a that pa1 points to and has type t; | | *pa1++ | references the value in a that pa1 points to before incrementing pa1 and has type t; | | pa1 + n | produces a pointer that points n elements further into a than pa1 and has type “pointer to t”; | | pa1 - n | produces a pointer to a that points n elements previous to that pointed to by pa1 and has type “pointer to t”; | | *(pa1 + n) | stores the value of v into the element pointed to by pa1 + n and has = v type t; | | pa1 < pa2 | tests whether pa1 is pointing to an earlier element in a than is pa2 and has type int (any relational operators can be used to compare two pointers); | | pa2 - pa1 | produces the number of elements in a contained between the pointers pa2 and pa1 (assuming that pa2 points to an element further in a than pa1) and has integer type; | | a + n | produces a pointer to element number n of a, has type “pointer to t,” and is in all ways equivalent to the expression &a[n]; | | *(a + n) | references element number n of a, has type t, and is in all ways equivalent to the expression a[n]. | The actual type of the integer produced by subtracting two pointers is specified by ptrdiff_t, which is defined in the standard header file <stddef.h>. Pointers to Structures Given that | x | | is an lvalue expression of type struct s;
| | ps | | is a modifiable lvalue expression of type “pointer to struct s”; | | m | | is the name of a member of the structure s and is of type t; | | v | | is an expression; | the expression | &x | | produces a pointer to x and is of type “pointer to struct s”;
| | ps = &x | | sets ps pointing to x and is of type “pointer to struct s”; | | ps->m | | references member m of the structure pointed to by ps and is of type t; | | (*ps).m | | also references this member and is in all ways equivalent to the expression ps->m; | | ps->m = v | | stores the value of v into the member m of the structure pointed to by ps and is of type t | Compound LiteralsA compound literal is a type name enclosed in parentheses followed by an initialization list. It creates an unnamed value of the specified type, which has scope limited to the block in which it is created, or global scope if defined outside of any block. In the latter case, the initializers must all be constant expressions. As an example, (struct point) {.x = 0, .y = 0} is an expression that produces a structure of type struct point with the specified initial values.This can be assigned to another struct point structure, like so: origin = (struct point) {.x = 0, .y = 0}; Or it can be passed to a function or method expecting an argument of struct point, like so: moveToPoint ((struct point) {.x = 0, .y = 0}); Types other than structures can be defined as well—for example, if intPtr is of type int *, the statement intPtr = (int [100]) {[0] = 1, [50] = 50, [99] = 99 }; (which can appear anywhere in the program) sets intptr pointing to an array of 100 integers, whose 3 elements are initialized as specified. If the size of the array is not specified, it is determined by the initializer list. Conversion of Basic Data TypesThe Objective-C language converts operands in arithmetic expressions in a predefined order, known as the usual arithmetic conversions: - If either operand is of type long double, the other is converted to long double and that is the type of the result.
- If either operand is of type double, the other is converted to double and that is the type of the result.
- If either operand is of type float, the other is converted to float and that is the type of the result.
- If either operand is of type _Bool, char, short int, int bit field, or an enumerated data type, it is converted to int, if an int can fully represent its range of values; otherwise, it is converted to unsigned int. If both operands are of the same type, that is the type of the result.
- If both operands are signed or both are unsigned, the smaller integer type is converted to the larger integer type and that is the type of the result.
- If the unsigned operand is equal in size or larger than the signed operand, the signed operand is converted to the type of the unsigned operand, and that is the type of the result.
- If the signed operand can represent all the values in the unsigned operand, the latter is converted to the type of the former if it can fully represent its range of values, and that is the type of the result.
- If this step is reached, both operands are converted to the unsigned type corresponding to the type of the signed type.
Step 4 is known more formally as integral promotion. Conversion of operands is well behaved in most situations, although the following points should be noted:
- Conversion of a char to an int can involve sign extension on some machines, unless the char is declared as unsigned.
- Conversion of a signed integer to a longer integer results in extension of the sign to the left; conversion of an unsigned integer to a longer integer results in zero fill to the left.
- Conversion of any value to a _Bool results in 0 if the value is zero and 1 otherwise.
- Conversion of a longer integer to a shorter one results in truncation of the integer on the left.
- Conversion of a floating-point value to an integer results in truncation of the decimal portion of the value. If the integer is not large enough to contain the converted floating-point value, the result is not defined, as is the result of converting a negative floating-point value to an unsigned integer.
- Conversion of a longer floating-point value to a shorter one might or might not result in rounding before the truncation occurs.
|