## 5.2. Operators in DetailThis section describes in detail the individual operators, and indicates what kinds of operands are permissible. The descriptions are arranged according to the customary usage of the operators, beginning with the usual arithmetic and assignment operators. ## 5.2.1. Arithmetic OperatorsTable 5-5 lists the arithmetic operators .
The operands of the arithmetic operators are subject to the following rules: Only the `%`operator requires integer operands.The operands of all other operators may have any arithmetic type.
Furthermore, addition and subtraction operations may also be performed on pointers in the following cases: In an addition, one addend can be an object pointer while the other has an integer type. In a subtraction, either both operands can be pointers to objects of the same type (without regard to type qualifiers), or the minuend (the left operand) can be an object pointer, while the subtrahend (the right operand) has an integer type.
## 5.2.1.1. Standard arithmeticThe operands are subject to the
If both operands in a multiplication or a division have the same sign, the result is positive; otherwise, it is negative. However, the result of a modulo operation always has the same sign as the left operand. For this reason, the expression ## 5.2.1.2. Pointer arithmeticYou can use the binary operators Adding an integer to or subtracting an integer from a pointer yields a pointer value with the same type as the pointer operand. The compiler automatically multiplies the integer by the size of the object referred to by the pointer type, as Example 5-1 illustrates. ## Example 5-1. Pointer arithmeticdouble dArr[5] = { 0.0, 1.1, 2.2, 3.3, 4.4 }, // Initialize an array and *dPtr = dArr; // a pointer to its first element. int i = 0; // An index variable. dPtr = dPtr + 1; // Advance dPtr to the second element. Addends dPtr = 2 + dPtr; // can be in either order. dPtr now points to dArr[3]. printf( "%.1f\n", *dPtr ); // Print the element referenced by dPtr. printf( "%.1f\n", *(dPtr -1) ); // Print the element before that, without // modifying the pointer dPtr. i = dPtr - dArr; // Result: the index of the array element that dPtr points to. Figure 5-1 illustrates the effects of the two assignment expressions using the pointer ## Figure 5-1. Using a pointer to move through the elements in an arrayThe statement The statement dPtr += 1; ++dPtr; dPtr++; Subtracting one pointer from another yields an integer value with the type For more information on pointer arithmetic, see Chapter 9. ## 5.2.2. Assignment OperatorsIn an assignment
operation, the left operand must be a There are also
## 5.2.2.1. Simple assignmentThe operands of a simple assignment must fulfill one of the following conditions: Both operands have arithmetic types. The left operand has the type `_Bool`and the right operand is a pointer.Both operands have the same structure or union type. Both operands are pointers to the same type, or the left operand is a pointer to a qualified version of the common typethat is, the type pointed to by the left operand is declared with one or more additional type qualifiers (see Chapter 11). One operand is an object pointer and the other is a pointer to `void`(here again, the type pointed to by the left operand may have additional type qualifiers).The left operand is a pointer and the right is a null pointer constant.
If the two operands have different types, the value of the right operand is converted to the type of the left operand (see the sections "The Results of Arithmetic Type Conversions" and "Implicit Pointer Conversions" in Chapter 4). The modification of the left operand is a side effect of an #include <stdio.h> char c = 0; /* ... */ while ( (c = getchar( )) != EOF ) { /* ... Process the character stored in c ... */ } In the controlling expression of the As Table 5-4 shows, assignment operators have a low precedence, and are grouped with their operators from right to left. As a result, no parentheses are needed around the expression to the right of the assignment operator, and multiple assignments can be combined in one expression, as in this example: double x = 0.5, y1, y2; // Declarations y1 = y2 = 10.0 * x; // Equivalent to y1 = (y2 = (10.0 * x)); This expression assigns the result of the multiplication to ## 5.2.2.2. Compound assignmentsA compound assignment is performed by any of the following operators: *= /= %= += -= In evaluating a compound assignment expression, the program combines the two operands with the specified operation and assigns the result to the left operand. Two examples: long var = 1234L ; var *= 3; // Triple the value of var. var <<= 2; // Shift the bit pattern in var two bit-positions to the // left (i.e., multiply the value by four). The only difference between a compound assignment
x[++i] *= 2; // Increment i once, then double the indexed // array element. x[++i] = x[++i] * (2); // Oops: you probably didn't want to increment i // twice. In the equivalent form double var1 = 2.5, var2 = 0.5; var1 /= var2 + 1; // Equivalent to var1 = var1 / (var2 + 1); Without the parentheses, the expression The operands of a compound assignment can have any types that are permissible for the operands of the corresponding binary operator. The only additional restriction is that when you add a pointer to an integer, the pointer must be the left operand, as the result of the addition is a pointer. Example: short *sPtr; /* ... */ sPtr += 2; // Equivalent to sPtr = sPtr + 2; // or sPtr = 2 + sPtr; ## 5.2.3. Increment and Decrement OperatorsEach of the tokens
These operators require a modifiable lvalue as their operand. More specifically, the operand must have a real arithmetic type (not a complex type), or an object pointer type. The expressions The following examples demonstrate the use of the increment operators, along with the subscript operator char a[10] = "Jim"; int i = 0; printf( "%c\n", a[i++] ); // Output: J printf( "%c\n", a[++i] ); // Output: m The character argument in the first The operator i = 0; printf( "%c\n", a[i]++ ); // Output: J printf( "%c\n", ++a[i] ); // Output: L According to the operator precedences and associativity in Table 5-4, the expressions The operators char a2[10], *p1 = a, *p2 = a2; // Copy string to a2: while ( (*p2++ = *p1++) != '\0' ) ; Because the postfix operator By contrast, the expression ## 5.2.4. Comparative OperatorsThe comparative
operators
, also called the
For all comparative operators, the operands must meet one of the following conditions: Both operands have real arithmetic types. Both operands are pointers to objects of the same type, which may be declared with different type qualifiers.
With the equality operators, The two operands have any arithmetic types, including complex types. Both operands are pointers to functions of the same type. One operand is an object pointer, while the other is a pointer to `void`. The two may be declared with different type qualifiers (the operand that is not a pointer to`void`is implicitly converted to the type`void*`for the comparison).One operand is a pointer and the other is a null pointer constant. The null pointer constant is converted to the other operand's type for the comparison.
The operands of all comparison operators are subject to the When you compare two object pointers, the result depends on the relative positions of the objects in memory. Elements of an array are objects with fixed relative positions: a pointer that references an element with a greater index is greater than any pointer that references an element with a lesser index. A pointer can also contain the address of the first memory location after the last element of an array. In this case, that pointer's value is greater than that of any pointer to an element of the array. The function in Example 5-2 illustrates some expressions with pointers as operands. ## Example 5-2. Operations with pointers/* The function average( ) calculates the arithmetic mean of the * numbers passed to it in an array. * Arguments: An array of float, and its length. * Return value: The arithmetic mean of the array elements, with type double. */ double average( const float *array, int length ) { double sum = 0.0; float *end = array + length; // Points one past the last element. if ( length <= 0 ) // The average of no elements is zero. return 0.0; // Accumulate the sum by for ( float *p = array; p < end; ++p ) // walking a pointer through the array. sum += *p; return sum/length; // The average of the element values. } Two pointers are equal if they point to the same location in memory, or if they are both null pointers. In particular, pointers to members of the same union are always equal, because all members of a union begin at the same address. The rule for members of the same structure, however, is that a pointer to if and only if member1 is declared after member2 in the structure type's definition.member1The comparative operators have lower precedence than the arithmetic operators, but higher precedence than the logical operators . As a result, the following two expressions are equivalent: a < b && b < c + 1 (a < b) && (b < (c + 1)) Furthermore, the equality operators, a < b != b < c (a < b) != (b < c) This expression is true (that is, it yields the value 1) if and only if one of the two operand expressions, ## 5.2.5. Logical OperatorsYou can connect expressions using logical operators to form compound conditions, such as those often used in jump and loop statements to control the program flow. C uses the symbols described in Table 5-10 for the boolean operations AND, OR, and NOT.
Like comparative expressions, logical expressions have the type The operands may have any scalar type desiredin other words, any arithmetic or pointer type. Any operand with a value of 0 is interpreted as (deviation < -0.2) || (deviation > 0.2) deviation < -0.2 || deviation > 0.2 !(deviation >= -0.2 && deviation <= 0.2) Each of these logical expressions yields the value 1, or true, whenever the value of the variable ( deviation < -0.2 || deviation > 0.2 ) && status == 1 Without the parentheses, that expression would be equivalent to this: deviation < -0.2 || ( deviation > 0.2 && status == 1 ) These expressions yield different results if, for example, The operators double x; _Bool get_x(double *x), check_x(double); // Function prototype // declarations. /* ... */ while ( get_x(&x) && check_x(x) ) // Read and test a number. { /* ... Process x ... */ } In the controlling expression of the ## 5.2.6. Bitwise OperatorsFor more compact data, C programs can store information in individual bits or groups of bits. File access permissions are a common example. The bitwise operators allow you to manipulate individual bits in a byte or in a larger data unit: you can clear, set, or invert any bit or group of bits. You can also shift the bit pattern of an integer to the left or right. The bit pattern of an integer type consists of bit positions numbered from right to left, beginning with position 0 for the least significant bit. For example, consider the Bit pattern 0 0 1 0 1 0 1 0 Bit positions 7 6 5 4 3 2 1 0 In this example, the value 101010 is shown in the context of an 8-bit byte; hence the two leading zeros. ## 5.2.6.1. Boolean bitwise operatorsThe operators listed in Table 5-11 perform Boolean operations on each bit position of their operands. The binary operators connect the bit in each position in one operand with the bit in the same position in the other operand. A bit that is set, or 1, is interpreted as In addition to the operators for boolean AND, OR, and NOT, there is also a bitwise exclusive-OR operator. These are all described in Table 5-11.
The operands of the bitwise operators must have integer types, and are subject to the usual arithmetic conversions. The resulting common type of the operands is the type of the result. Table 5-12 illustrates the effects of these operators.
You can clear certain bits in an integer variable a &= 0xFF; // Equivalent notation: a = a & 0xFF; As this example illustrates, the compound assignment operator The bitwise operators are also useful in making bit masks
to use in further bit operations. For example, in the bit pattern of a &= ~0x20; // Clear bit 5 in a. The bit mask You can also use the operators int mask = 0xC; a |= mask; // Set bits 2 and 3 in a. a ^= mask; // Invert bits 2 and 3 in a. A second inversion using the same bit mask reverses the first inversion. In other words, a ^= b; // Equivalent to a = a ^ b; b ^= a; // Assign b the original value of a. a ^= b; // Assign a the original value of b. The first two expressions in this example are equivalent to ## 5.2.6.2. Shift operatorsThe shift operators transpose the bit pattern of the left operand by the number of bit positions indicated by the right operand. They are listed in Table 5-13.
The operands of the shift operators must be integers. Before the actual bit-shift, the integer promotions are performed on both operands. The value of the right operand must not be negative, and must be less than the width of the left operand after integer promotion. If it does not meet these conditions, the program's behavior is undefined. The result has the type of the left operand after integer promotion. The shift expressions in the following example have the type unsigned long n = 0xB, // Bit pattern: 0 ... 0 0 0 1 0 1 1 result = 0; result = n << 2; // 0 ... 0 1 0 1 1 0 0 result = n >> 2; // 0 ... 0 0 0 0 0 1 0 In a left shift, the bit positions that are vacated on the right are always cleared. Bit values shifted beyond the leftmost bit position are lost. A left shift through ^{y}: If the left operand has an unsigned type, then the expression xx<< yields the value of y x 2x^{y}. Thus in the previous example, the expression n << 2 yields the value of n x 4, or 44.On a right shift, the vacated bit positions on the left are cleared if the left operand has an unsigned type, or if it has a signed type and a non-negative value. In this case, the expression has /2x^{y}. If the left operand has a negative value, then the fill value depends on the compiler: it may be either zero or the value of the sign bit.The shift operators are useful in generating certain bit masks. For example, the expression ## Example 5-3. Using a shift operation to manipulate a bit mask// Function setBit( ) // Sets the bit at position p in the mask m. // Uses CHAR_BIT, defined in limits.h, for the number of bits in a byte. // Return value: The new mask with the bit set, or the original mask // if p is not a valid bit position. unsigned int setBit( unsigned int mask, unsigned int p ) { if ( p >= CHAR_BIT * sizeof(int) ) return mask; else return mask | (1 << p); } The shift operators
have lower precedence than the arithmetic operators, but higher precedence than the comparative operators and the other bitwise operators. The parentheses in the expression ## 5.2.7. Memory Addressing OperatorsThe five operators listed in Table 5-14 are used in addressing array elements and members of structures, and in using pointers to access objects and functions.
## 5.2.7.1. The & and * operatorsThe &x has the type "pointer to ."TThe operand of the address operator must have an addressable location in memory. In other words, the operand must designate either a function or an object (i.e., an lvalue) that is not a bit-field, and has not been declared with the storage class You need to obtain the addresses of objects and functions when you want to initialize pointers to them: float x, *ptr; ptr = &x; // OK: Make ptr point to x. ptr = &(x+1); // Error: (x+1) is not an lvalue. Conversely, when you have a pointer and want to access the object it references, use the float x, *ptr = &x; *ptr = 1.7; // Assign the value 1.7 to the variable x ++(*ptr); // and add 1 to it. In the final statement of this example, the value of The behavior of the indirection operator Like the other unary operators, the operators The operators . However, ptr&* is never an lvalue, even if ptr is.ptr## 5.2.7.2. Elements of arraysThe The left operand of ## Example 5-4. Initializing an array#include <stdlib.h> #define ARRAY_SIZE 100 /* ... */ double *pArray = NULL; int i = 0: pArray = malloc( ARRAY_SIZE * sizeof(double) ); // Generate the array if ( pArray != NULL ) { for ( i = 0; i < ARRAY_SIZE; ++i ) // and initialize it. pArray[i] = (double)rand( )/RAND_MAX; /* ... */ } In Example 5-4, the expression ## 5.2.7.3. Members of structures and unionsThe binary operators As Example 5-5 illustrates, the left operand of the dot operator ## Example 5-5. The dot operatorstruct Article { long number; // The part number of an article char name[32]; // The article's name long price; // The unit price in cents /* ... */ }; struct Article sw = { 102030L, "Heroes", 5995L }; sw.price = 4995L; // Change the price to 49.95 The result of the dot operator has the value and type of the selected member. If the left operand is an lvalue, then the operation also yields an lvalue. If the left operand has a qualified type (such as one declared with The left operand of the dot operator is not always an lvalue, as the following example shows: struct Article getArticle( ); // Function prototype printf( "name: %s\n", getArticle( ).name ); The function The operator ## Example 5-6. The arrow operatorstruct Article *pArticle = &sw, // A pointer to struct Article. const *pcArticle = &sw; // A "read-only pointer" to struct // Article. ++(pArticle->number); // Increment the part number. if ( pcArticle->number == 102031L ) // Correct usage: read-only access. pcArticle->price += 50; // Error: can't use a const-qualified // pointer to modify the object. The result of the arrow operator is always an lvalue. It has the type of the selected member, as well as any type qualifications of the pointer operand. In Example 5-6, Any expression that contains the arrow operator can be rewritten using the dot operator by dereferencing the pointer separately: an expression of the form The operators To conclude this section, we can combine the subscript, dot, and arrow operators to work with an array whose elements are structures: struct Article arrArticle[10]; // An array with ten elements // of type struct Article. arrArticle[2].price = 990L; // Set the price of the // array element arrArticle[2]. arrArticle->number = 10100L; // Set the part number in the // array element arrArticle[0]. An array name, such as arrArticle[i].number (arrArticle+i)->number (*(arrArticle+i).number All of them designate the member ## 5.2.8. Other OperatorsThere are six other operators in C that do not fall into any of the categories described in this chapter. Table 5-15 lists these operators in order of precedence.
## 5.2.8.1. Function callsA function call is an expression of the form void.Before you can call a function, you must make sure that it has been declared in the same translation unit. Usually a source file includes a header file containing the function declaration, as in this example: #include <math.h> // Contains the prototype double pow( double, double ); double x = 0.7, y = 0.0; /* ... */ y = pow( x+1, 3.0 ); // Type: double The parentheses enclose the comma-separated list of arguments passed to the function, which can also be an empty list. If the function declaration is in prototype form (as is usually the case), the compiler ensures that each argument is converted to the type of the corresponding parameter, as for an assignment. If this conversion fails, the compiler issues an error message: pow( x, 3 ); // The integer constant 3 is converted to type double. pow( x ); // Error: incorrect number of arguments. The order in which the program evaluates the individual expressions that designate the function and its arguments is not defined. As a result, the behavior of a int i = 0; printf( "%d %d\n", i, ++i ); // Behavior undefined However, there is a sequence point after all of these expressions have been evaluated and before control passes to the function. Like the other postfix operators, a function call has the highest precedence, and is grouped with operands from left to right. For example, suppose that fn_table[i++]( ).price The expression calls the function referenced by the pointer stored in Chapter 7 describes function calls in more detail, including recursive functions and functions that take a variable number of arguments. ## 5.2.8.2. Compound literalsCompound literals are an extension introduced in the C99 standard. This extension allows you to define literals with any object type desired. A ( The value of the expression is an unnamed object that has the specified type and the values listed. If you place a compound literal outside of all function blocks, then the initializers must be constant expressions, and the object has static storage duration. Otherwise it has automatic storage duration, determined by the containing block. Typical compound literals generate objects with array or structure types. Here are a few examples to illustrate their use: float *fPtr = (float [ ]){ -0.5, 0.0, +0.5 }; This declaration defines a pointer to a nameless array of three float elements. #include "database.h" // Contains prototypes and type definitions, // including the structure Pair: // struct Pair { long key; char value[32]; }; insertPair( &db, &(struct Pair){ 1000L, "New York JFK Airport" } ); This statement passes the address of a literal of type To define a constant compound literal, use the type qualifier (const char [ ]){"A constant string."} If the previous expression appears outside of all functions, it defines a static array of "A constant string." In fact, the compiler may store string literals and constant compound literals with the same type and contents at the same location in memory. Despite their similar appearance, compound literals are not the same as cast expressions. The result of a cast expression has a scalar type or the type ## 5.2.8.3. The sizeof operatorThe The operand of the For example, if sizeof(int) sizeof i sizeof(i) sizeof *iPtr sizeof(*iPtr) Note the difference to the following expressions, each of which yields the size of a pointer to sizeof(*int) sizeof &i sizeof(&i) sizeof iPtr sizeof(iPtr) Like For an operand with the type struct gap { char version; short value; }; In the following example, the standard function #include <string.h> /* ... */ struct gap g; memset( &g, 0, sizeof g ); If the operand of ## Example 5-7. Sizing variable-length arraysvoid func( float a[ ], int n ) { float b[2*n]; // A variable-length array of float. /* ... the value of n may change now ... */ int m = sizeof(b) / sizeof(*b); // Yields the number of elements /* ... */ // in the array b. } Regardless of the current value of the variable
## 5.2.8.4. The conditional operatorThe
The operation first evaluates the condition. Then, depending on the result, it evaluates one or the other of the two alternative expressions. There is a sequence point after the is true), then only the second operand, condition, is evaluated, and the entire operation yields the value of expression 1. If on the other hand expression 1 does yield 0 (i.e., conditionfalse), then only the third operand, , is evaluated, and the entire operation yields the value of expression 2. In this way the conditional operator represents a conditional jump in the program flow, and is therefore an alternative to some expression 2if-else statements.A common example is the following function, which finds the maximum of two numbers: inline int iMax(int a, int b) { return a >= b ? a : b; } The function inline int iMax(int a, int b) { if ( a >= b ) return a; else return b; } The conditional operator has a very low precedence: only the assignment operators and the comma operator are lower. Thus the following statement requires no parentheses: distance = x < y ? y - x : x - y; The first operand of the conditional operator, and expression 1, must fulfill one of the following cases:expression 2Both of the alternative expressions have arithmetic types, in which case the result of the complete operation has the type that results from performing the usual arithmetic conversions on these operands. Both of the alternative operands have the same structure or union type, or the type `void`. The result of the operation also has this type.Both of the alternative operands are pointers, and one of the following is true: Both pointers have the same type. The result of the operation then has this type as well. One operand is a null pointer constant. The result then has the type of the other operand. One operand is an object pointer and the other is a pointer to `void`. The result then has the type`void *`.
The two pointers may point to differently qualified types. In this case, the result is a pointer to a type which has all of the type qualifiers of the two alternative operands. For example, suppose that the following pointers have been defined: const int *cintPtr; // Declare pointers. volatile int *vintPtr; void *voidPtr; The expressions in the following table then have the type indicated, regardless of the truth value of the variable
## 5.2.8.5. The comma operatorThe comma operator is a binary operator:
The comma operator ensures sequential processing: first the left operand is evaluated, then the right operand. The result of the complete expression has the type and value of the right operand. The left operand is only evaluated for its side effects; its value is discarded. There is a sequence point after the evaluation of the left operand. Example: x = 2.7, sqrt( 2*x ) In this expression, the assignment takes place first, before the The comma operator has the lowest precedence of all operators. For this reason, the assignment y = ( x = 2.7, sqrt( 2*x )); This statement assigns the square root of 5.4 to A comma in a list of initializers or function arguments is a list separator, not a comma operator. In such contexts, however, you can still use a comma operator by enclosing an expression in parentheses: y = sqrt( (x=2.7, 2*x) ); This statement is equivalent to the one in the previous example. The comma operator allows you to group several expressions into one. This ability makes it useful for initializing or incrementing multiple variables in the head of a int i; float fArray[10], val; for ( i=0, val=0.25; i < 10; ++i, val *= 2.0 ) fArray[i] = val; |