A summary of all of the operators available to C was introduced in the section on language constructs, but they now deserve a fuller exposition.
If more than one operator appears in an expression, the order of evalulation is determined by the operators' level of precedence. For example, 5+3*2 is evaluated as 5 + (3*2) because the precedence of the multiplication operator, 3, causes it to be evaluted before the addition operator (precedence 4).
(Parentheses) have amongst the highest precedence, so to change the order of evaluation given above, one can simply write (5+3) * 2 as expected.
Where operators of the same precedence occur in an expression, the order of evaluation is determined by rules of associativity. Most operators associate from left to right, so a + b - c means (a + b) - c because + and - have the same precedence, and are left-to-right associative. Direction of association is important. If the assignment operator associated left-to-right, it wouldn't be possible to write x = y = 2 because that would be interpreted as (x=y) = 5 which is a syntax error (because x=y is not an lvalue which is to say one cannot assign to it). Fortunately, the right-to-left association of = makes sure the result is x = (y=5), which is fine.
Most operators in C are binary, infix operators. This means that they take exactly two arguments, and the operator goes inbetween them. We write 2 + 3, not + 2 3 (because + is not a prefix operator) nor 2 3 + (because + is not a postfix operator).
Unary -, unary +, bitwise and logical "not" (~, !), reference and dereference (&, *), increment and decrement (++, --), sizeof, and typecast operators are all unary, which is to say they take exactly one argument.
C has only one ternary operator: the conditional operator. It is sometimes referred to, rather imprecisely, as "the ternary operator".
Apart from the four obvious arithmetic operators *, /, + and -, there is a remainder operator %. All of these can be appiled to integers and floating-point values: the normal type-promotion rules apply.
It is often required to increment or decrement a numerical value by 1. Because it becomes very tedious to write expressions like a = a + 1, the increment and decrement operators are made available. This permits the above to be abbreviated to ++a or a++. Similarly, to reduce the value of a variable, one can write --a or a--.
Because the increment and decrement operators really mean "increase to the next greatest value" or "reduce to the next lower value", they cannot be applied to floating point values. Instead, one can write x -= 1.0 to reduce the value held in a floating point variable by 1.0 (see Assignment below).
The increment and decrement operators can be used before the variable or after it (i.e. as prefix or postfix operators). There is a subtle difference which becomes very important when they are used as part as a larger expression.
In the postfix form, the value of the variable is used and then its value is changed. In the prefix form, the value of the variable is altered before it is used in evaluating the rest of the expression. The following example might help.
Many computer languages have only one assignment operator, usually = (like C) or :=. As well as simple assignment, C offers a shorthand for all of the binary operators when they are used directly with an assignment statement. The result are the operators +=, -=, *=, /=, %=, >>=, <<=, &=, ^= and |=. For example, one can write x /= 3 rather than the longhand x = x / 3.
The benefit becomes more obvious when the assignment target is a bit more of a mouthful than just "x"; this example from the Linux kernel:
hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase);
This might be bad enough, but is much better than the equivalent
hostdata->time_write[cmd->target] =
hostdata->time_write[cmd->target] + (jiffies - hostdata->timebase);
Logical operators are used only with int or char operands. They mostly used in conditional statements (see Program Control) where it might be desired to execute a particular statement only if condition 1 AND condition 2 both hold.
When C evaluates a boolean expression using logical operators, any non-zero value is considered true and zero is considered false.
The operators are: ! (Logical NOT), && (Logical AND), || (Logical OR).
The following table shows shows a few examples of how logical operators can be combined to produce boolean values.
| int x = 0; !x; |
TRUE |
| !0 | TRUE |
| !2 | FALSE |
| 1 && 2 | TRUE |
| 0 && 2 | FALSE | 0 || 2 | TRUE |
| (9 > 4) && !(5 > 8) | TRUE |

This sounds innocent enough, but consider the expression x > 0 && y++. If the initial value of y is 10, what is the value of y after the expression has been evaluated?
The answer depends on the value of x. If it is negative, the first term will be false, so the second does not need to be evaluated, so y remains unchanged. If x is non-negative, the result of the whole expression depends on y, which is incremented after its value is read. So the answer is "10 or 11".
This is a very bad way of incrementing y depending upon the value of x. So bad, in fact, that it is almost certainly not what the programmer intended and is a potential bug, unless an expectation of this bizare behaviour is expressed in comments. Remember: clever programming is bad. Avoid operators with side-effects in boolean expressions.
Relational operators compare the values of two expressions and return 1 if the relation holds and zero if it does not. The values returned may therefore be freely manipulated using the logical operators described above.
The following relational operators are available:
| == | Equal to |
| != | Not equal to |
| > | Greater than |
| >= | Greater than or equal |
| < | Less than |
| <= | Less than or equal |
There is an intrinsic danger in carrying out floating point tests for equality. It is quite legal to write x == 10.0 which would be expected to evaluate to TRUE only if x is exactly 10.0. However, rounding errors could cause an inequality when not expected. This is particularly problematic since some numbers which are very easily expressed in decimal, such as 0.2, turn out to be recurring when expressed in binary.
If possible, therefore, avoid tests for equality between floating point numbers. If you have to do them, round the number to an appropriate resolution before performing the test (in the above example, one might test fabs(x-10.0) < 0.001. Alternatively, if the test is a termination condition for an iterative algorithm, try to recast the program so that a much less dangerous test like x <= 10.0 can be used instead. Best of all, use integer arithmetic.
It is quite easy to make the error of typing = where == was intended.
Bitwise operations allow the testing, setting, clearing or shifting of bits within int or char operands.
| ~ | Bitwise one's complement NOT |
| << | Bitwise left shift |
| >> | Bitwise right shift |
| & | Bitwise AND |
| ^ | Bitwise eXclusive OR |
| | | Bitwise inclusive OR |
The table below shows example expressions involving bitwise operators between two unsigned character variables.
| Expression | Binary | Decimal | Comment |
|---|---|---|---|
| A | 01010101 | 85 | |
| B | 00011101 | 29 | |
| A&B | 00010101 | 21 | Bitwise AND |
| A|B | 01011101 | 93 | Bitwise OR |
| A^B | 01001000 | 72 | Bitwise XOR |
| ~A | 10101010 | 170 | Bitwise NOT |
| B<<2 | 01110100 | 116 | Left Shift |
| B>>2 | 00000111 | 14 | Right Shift |
The left-shift operator causes the bit pattern in the first operand to be shifted left the number of bits specified by the second operand. Bits vacated by the shift operation are zero-filled.
The right-shift operator causes the bit pattern in the first operand to be shifted right the number of bits specified by the second operand. Bits vacated by the shift operation are zero-filled for unsigned quantities. For signed quantities, the sign bit is propagated into the vacated bit positions.
Bitwise operators are frequently used to test, set or reset individual bits. This can be useful to store arbitrary flags in a C program, or to perform hardware-oriented operations in an on-chip register of a peripheral IC of an embedded system. For example, a word processor might need to store whether a chacter is bold, italic, neither or both. The programmer might use the following code:

Unfortunately, in spite of the fact that it is cheap easy to port compilers like gcc to even the simplest of processors, companies which build microcontroller development hardware frequently ship home-grown, half-baked compilers which don't support lesser-used C features, or support them badly. However, if a proper compiler is available, the programmer has the choice of implementing the above code as shown in the following session:
You can't use this as a trick to make very long integers by saying int x:128 for example. gcc just says "warning: width of `italic' exceeds its type", and your code probably won't work. Most compilers refuse to split bit fields over word boudaries, so declaring three such integers of 20 bits each will probably end up using 3 32-bit words of storage (on a 32-bit machine) rather than two, because otherwise the second 20-bit entitiy would overlap a word boundary. In this case, padding is inserted by the compiler automatically.
Of course, this doesn't come for free: the machine has to perform many extra operations to isolate the bitfields in a single byte or word. This technique should therefore be saved for special occasions when it is really needed. Operating on the bits in I/O registers is perhaps such a situation.
The ternary operator ? can replace the if...else form for certain statements. It takes the general form: expression1 ? expression2 : expression3. expression1 is evaluated. If it is TRUE, then expression2 is evaluated and its value becomes the value of the whole expression. If it is FALSE expression3 is evaluated insteasd, and its value becomes the value of the expression.
This section has covered the different operators available in the C language in depth. Some of the stylistic points are rather advanced, and rely on concepts not introduced; nevertheless, it would be out of place to locate them elsewhere. As the concepts are fundamental, it would be beneficial to revist this section towards the end of the course to ensure the material is thoroughly understood.
The tools upon which this course relies are Copyright the Free Software Foundataion where they are made available under the GPL (GNU Public Licence).
The content of this course was derived from that generated by many ex-colleagues at the University of Leeds, Department of Electronics and Electrical Engineering. Much of the content has been reworked, and substantially augmented, but Dr N J Bailey, Centre for Music Technology, The University of Glagsow. This manifestation is Copyright N J Bailey; some of the content is Copyright The University of Leeds.
Diagrams on this resource are drawn in XFig and are rendered by the browser using The University of Hamburg's Simple FIG viewer applet which is Copyright (C) 1996-2002 F.N.Hendrich, hendrich@informatik.uni-hamburg.de.
The source code, programming examples and exercises are all specific to this course, and are Copyright, Dr N J Bailey.
The applet for viewing and demonstrating C programs is Copyright Dr N J Bailey, and is to be found documented and with its source code on the Centre for Music Technology website under Software