One of the most important features of C is that it has a very rich set of built in operators including arithmetic, relational, logical, and bitwise operators.
int x ; x = 20 ;
Some common notation :-
lvalue -- left hand side of an assignment operation rvalue -- right hand side of an assignment operation
Type Conversions :- The value of the right hand side of an assignment is converted to the type of the lvalue. This may sometimes yield compiler warnings if information is lost in the conversion.
For Example :-
int x ; char ch ; float f ;
ch = x ; /* ch is assigned lower 8 bits of x, the remaining bits are discarded so we have a possible information loss */
x = f ; /* x is assigned non fractional part of f only within int range, information loss possible */
f = x ; /* value of x is converted to floating point */
Multiple assignments are possible to any degree in C, the assignment operator has right to left associativity which means that the rightmost expression is evaluated first.
For Example :-
x = y = z = 100 ;
In this case the expression z = 100 is carried out first. This causes the value 100 to be placed in z with the value of the whole expression being 100 also. This expression value is then taken and assigned by the next assignment operator on the left i.e. x = y = ( z = 100 );
+ - * / --- same rules as mathematics with * and / being evaluated before + and -.% -- modulus / remainder operator
For Example :-
int a = 5, b = 2, x ; float c = 5.0, d = 2.0, f ; x = a / b ; // integer division, x = 2. f = c / d ; // floating point division, f = 2.5. x = 5 % 2 ; // remainder operator, x = 1. x = 7 + 3 * 6 / 2 - 1 ;// x=15,* and / evaluated ahead of + and -.
Note that parentheses may be used to clarify or modify the evaluation of expressions of any type in C in the same way as in normal arithmetic.
x = 7 + ( 3 * 6 / 2 ) - 1 ; // clarifies order of evaluation without penalty x = ( 7 + 3 ) * 6 / ( 2 - 1 ) ; // changes order of evaluation, x = 60 now.
There are two special unary operators in C, Increment ++, and Decrement -- , which cause the variable they act on to be incremented or decremented by 1 respectively.
For Example :- x++ ; /* equivalent to x = x + 1 ; */
++ and -- can be used in prefix or postfix notation. In prefix notation the value of the variable is either incremented or decremented and is then read while in postfix notation the value of the variable is read first and is then incremented or decremented.
For Example :- int i, j = 2 ; i = ++ j ; /* prefix :- i has value 3, j has value 3 */ i = j++ ; /* postfix :- i has value 3, j has value 4 */
Many C operators can be combined with the assignment operator as shorthand notation
For Example :- x = x + 10 ; can be replaced by x += 10 ; Similarly for -=, *=, /=, %=, etc.
These shorthand operators improve the speed of execution as they require the expression, the variable x in the above example, to be evaluated once rather than twice.
The full set of relational operators are provided in shorthand notation
> >= < <= == !=
For Example :- if ( x == 2 ) printf( “x is equal to 2”) ;
&& -- Logical AND | | -- Logical OR ! -- Logical NOT
For Example :- if ( x >= 0 && x < 10 ) printf( “ x is greater than or equal to zero and less than ten.” ) ;
NOTE : There is no Boolean type in C so TRUE and FALSE are deemed to have the following meanings. FALSE -- value zero TRUE -- any non-zero value but 1 in the case of in-built relational operations
For Example :- 2 > 1 -- TRUE so expression has value 1 2 > 3 -- FALSE so expression has value 0 i = 2 > 1 ; -- relation is TRUE -- has value 1, i is assigned value 1
NOTE : Every C expression has a value. Typically we regard expressions like 2 + 3 as the only expressions with actual numeric values. However the relation 2 > 1 is an expression which evaluates to TRUE so it has a value 1 in C. Likewise if we have an expression x = 10 this has a value which in this case is 10 the value actually assigned.
NOTE : Beware of the following common source of error. If we want to test if a variable has a particular value we would write for example
if ( x == 10 ) … But if this is inadvertently written as if ( x = 10 ) …
this will give no compilation error to warn us but will compile and assign a value 10 to x when the condition is tested. As this value is non-zero the if condition is deemed true no matter what value x had originally. Obviously this is possibly a serious logical flaw in a program.
These are special operators that act on char or int arguments only. They allow the programmer to get closer to the machine level by operating at bit-level in their arguments.
& Bitwise AND | Bitwise OR ^ Bitwise XOR ~ Ones Complement >> Shift Right << Shift left
Recall that type char is one byte in size. This means it is made up of 8 distinct bits or binary digits normally designated as illustrated below with Bit 0 being the Least Significant Bit (LSB) and Bit 7 being the Most Significant Bit (MSB). The value represented below is 13 in decimal.
|Bit 7||Bit 6||Bit 5||Bit 4||Bit 3||Bit 2||Bit 1||Bit 0|
An integer on a 16 bit OS is two bytes in size and so Bit 15 will be the MSB while on a 32 bit system the integer is four bytes in size with Bit 31 as the MSB.
RULE : If any two bits in the same bit position are set then the resultant bit in that position is set otherwise it is zero.
For Example :- 1011 0010 (178) & 0011 1111 (63) = 0011 0010 (50)
RULE : If either bit in corresponding positions are set the resultant bit in that position is set.
For Example :- 1011 0010 (178) | 0000 1000 (63) = 1011 1010 (186)
RULE : If the bits in corresponding positions are different then the resultant bit is set.
For Example :- 1011 0010 (178) ^ 0011 1100 (63) = 1000 1110 (142)
RULE : These move all bits in the operand left or right by a specified number of places.
variable << number of places variable >> number of places
For Example :- 2 << 2 = 8 i.e. 0000 0010 becomes 0000 1000
NOTE : shift left by one place multiplies by 2 shift right by one place divides by 2
RULE : Reverses the state of each bit.
For Example :- 1101 0011 becomes 0010 1100
NOTE : With all of the above bitwise operators we must work with decimal, octal, or hexadecimal values as binary is not supported directly in C.
The bitwise operators are most commonly used in system level programming where individual bits of an integer will represent certain real life entities which are either on or off, one or zero. The programmer will need to be able to manipulate individual bits directly in these situations.
A mask variable which allows us to ignore certain bit positions and concentrate the operation only on those of specific interest to us is almost always used in these situations. The value given to the mask variable depends on the operator being used and the result required.
For Example :- To clear bit 7 of a char variable. char ch = 89 ; // any value char mask = 127 ; // 0111 1111 ch = ch & mask ; // or ch &= mask ;
For Example :- To set bit 1 of an integer variable. int i = 234 ; // any value int mask = 2 ; // a 1 in bit position 2 i |= mask ;
Normally in mixed type expressions all operands are converted temporarily up to the type of the largest operand in the expression.
Normally this automatic or implicit casting of operands follows the following guidelines in ascending order.
For Example :- int i ; float f1, f2 ; f1 = f2 + i ;
Since f2 is a floating point variable the value contained in the integer variable is temporarily converted or cast to a floating point variable also to standardise the addition operation in this case. However it is important to realise that no permanent modification is made to the integer variable.
Explicit casting coerces the expression to be of specific type and is carried out by means of the cast operator which has the following syntax.
( type ) expression
For Example if we have an integer x, and we wish to use floating point division in the expression x/2 we might do the following
( float ) x / 2
which causes x to be temporarily cast to a floating point value and then implicit casting causes the whole operation to be floating point division.
The same results could be achieved by stating the operation as
x / 2.0
which essentially does the same thing but the former is more obvious and descriptive of what is happening.
NOTE : It should be noted that all of these casting operations, both implicit and explicit, require processor time. Therefore for optimum efficiency the number of conversions should be kept to a minimum.
The sizeof operator gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types as we will see later on).
The expression is either an identifier or a type-cast expression (a type specifier enclosed in parentheses).
sizeof ( expression )
For Example :- int x , size ; size = sizeof ( x ) ; printf(“The integer x requires %d bytes on this machine”, size); printf( “Doubles take up %d bytes on this machine”, sizeof ( double ) ) ;
When several operations are combined into one C expression the compiler has to rely on a strict set of precedence rules to decide which operation will take preference. The precedence of C operators is given below.
|Highest||( ) [ ] -> .||left to right|
|! ~ ++ -- +(unary) -(unary) (type) * & sizeof||right to left|
|* / %||left to right|
|+ -||left to right|
|<< >>||left to right|
|< <= > >=||left to right|
|== !=||left to right|
|&||left to right|
|^||left to right|
||||left to right|
|&&||left to right|
|||||left to right|
|? :||left to right|
|= += -= *= /= %= &= ^= |= <<= >>=||right to left|
|Lowest||,||left to right|
Operators at the top of the table have highest precedence and when combined with other operators at the same expression level will be evaluated first.
For example take the expression 2 + 10 * 5 ;
Here * and + are being applied at the same level in the expression but which comes first ? The answer lies in the precedence table where the * is at a higher level than the + and so will be applied first.
When two operators with the same precedence level are applied at the same expression level the associativity of the operators comes into play.
For example in the expression 2 + 3 - 4 ;
the + and - operators are at the same precedence level but associate from left to right and so the addition will be performed first. However in the expression
x = y = 2;
as we have noted already the assignment operator associates from right to left and so the rightmost assignment is first performed.
NOTE : As we have seen already parentheses can be used to supersede the precedence rules and force evaluation along the lines we require. For example to force the addition in 2 + 10 * 5 ; to be carried out first we would write it as (2 + 10) * 5;