Operators
An operator is a symbol that represents a particular operation that can be performed on some data. The data is called an operand. The operator thus operates on an operand. Operators could be classified as “unary”, “binary” or “ternary” depending on the number of operands i.e, one, two, or three respectively.
- Unary expression: A unary expressionT contains one operand and a unary operator.
- Binary expression: A binary expressionT contains two operands separated by one operator.
Unary operators
Unary incerement and binary operators
The unary increment operator (++) increments the value of the operand by 1. Similarly the unary decrement operator (–) decrements the value by 1.
int x = 0; int p = 10; x = p++ ; -----------> Result: x = 10 // Now p will have a value 11. (Postfixing) x = ++p; -----------> Result : x = 12 // Now p will have a value 12. (Prefixing) p = 11 -----------> p = 11
Postfixing: The unary operators (increment or decrement) when used after the variable, as in p++, acts as a postfix operator. In the expression p++, p is incremented after its value has been used i.e., assigned to x.
Prefixing: The unary operators (increment or decrement) when used before the variable, as in ++p, acts as a prefix operator. The expression ++p increments p before its value has been used i.e., assigned to x.
The table below contains some more examples of unary operators.
Unary plus operator +
The T+T (unary plus) operator maintains the value of the operand. The operand can have any arithmetic type or pointer type.
Unary minus operator
The T-T (unary minus) operator negates the value of the operand. The operand can have any arithmetic type. For example, if TqualityT has the value T100T, T-qualityT has the value T-100T.
Logical negation operator !
The expression yields the value 1 (true) if the operand evaluates to 0, and yields the value 0 (false) if the operand evaluates to a nonzero value.
Bitwise negation operator ~
The T~T (bitwise negation) operator yields the bitwise complement of the operand. In the binary representation of the result, every bit has the opposite value of the same bit in the binary representation of the operand. The operand must have an integral type. The result has the same type as the operand but is not an lvalue.
Suppose TxT represents the decimal value T5T. The 16-bit binary representation of TxT is:0000000000000101. The expression T~xT yields the following result (represented here as a 16-bit binary number):1111111111111010.
Address operator &
The T&T (address) operator yields a pointer to its operand. If Tp_to_yT is defined as a pointer to an TintT and TyT as an TintT, the following expression assigns the address of the variable TyT to the pointer Tp_to_yT:
p_to_y = &y;
Indirection operator *
The T*T (indirection) operator determines the value referred to by the pointer-type operand. If Tp_to_yT is defined as a pointer to an TintT and TyT as an TintT, the expressions:
p_to_y = &y; *p_to_y = 3;
cause the variable TyT to receive the value T3T.
The sizeof operator
The sizeof operator returns the number of bytes the operand occupies in memory. The operand may be a variable, a constant or a data type qualifier.
/* sample program using sizeof operator */# include <stdio.h> void main(void) { int sum; printf(“%d \n”, sizeof(float)); printf(“%d \n”, sizeof(sum)); printf(“%d \n”, sizeof(char)); printf(“%d \n”, sizeof(‘G’)); }
The output of the above program will be compiler-dependent. The sizeof operator is generally used to determine the lengths of entities called arrays and structures when their sizes are not known. It is also used to allocate memory dynamically during program execution.
Binary operators
Arithmetic operators
The binary arithmetic operators are +, -, *, / and the modulus operator %. Integer division truncates any fractional part. The modulus operator returns the remainder of the integer division. This operator is applicable only for integers and cannot be applied to float or double.
The operators *, / and % all have the same priority, which is higher than the priority of binary addition (+) and subtraction (-). In case of an expression containing the operators having the same precedence, it gets evaluated from left to right. This default precedence can be overridden by using a set of parentheses. If there is more than one set of parentheses, the innermost parentheses will be performed first, followed by the operations within the second innermost pair and so on.
34 + 5 = 39 12 – 7 = 5 15 * 5 = 75 14 / 8 = 1 17 % 6 = 5
Relational operators
Relational operators are used to compare two operands to check whether they are equal, unequal or one is greater than or less than the other.
The value of the relational expression is of integer type and is 1, if the result of comparison is true and 0 if it is false.
14 > 8 has the value 1, as it is true 34 <= 19 has the value 0, as it is false
Logical operators
The logical operators && (AND), || (OR) allow two or more expressions to be combined to form a single expression. The expressions involving these operators are evaluated left to right and evaluation stops as soon as the truth or the falsehood of the result is known.
Bitwise operators
The bitwise operators provided by C may only be applied to operands of type char, short, int and long, whether signed or unsigned.
& AND | OR ^ XOR ~ one's compliment << Shift Left >> Shift Right
AND
AND & will copy a bit to the result if it exists in both operands.
#include<stdio.h> main() { unsigned int a = 60; /* 60 = 0011 1100 */ unsigned int b = 13; /* 13 = 0000 1101 */ unsigned int c = 0; c = a & b; /* 12 = 0000 1100 */}
OR
OR | will copy a bit if it exists in either operand:
#include<stdio.h> main() { unsigned int a = 60; /* 60 = 0011 1100 */ unsigned int b = 13; /* 13 = 0000 1101 */ unsigned int c = 0; c = a | b; /* 61 = 0011 1101 */ }
XOR
XOR ^ copies the bit if it is set in one operand (but not both):
#include<stdio.h> main() { int One = 20; int Two = 12; printf("One = %d Two = %d\n", One, Two); One ^= Two; Two ^= One; One ^= Two; printf("One = %d Two = %d\n", One, Two); return 0; }
The contents of two variables are swapped without the use of a temporary variable.
Ones Complement
This operator is unary (requires one operand) and has the effect of ‘flipping’ bits.
#include<stdio.h> main() { unsigned int Value=4; /* 4 = 0000 0100 */ Value = ~ Value; /* 251 = 1111 1011 */ }
Left Shift
The left operands value is moved left by the number of bits specified by the right operand.
#include<stdio.h> main() { unsigned int Value=4; /* 4 = 0000 0100 */ unsigned int Shift=2; Value = Value << Shift; /* 16 = 0001 0000 */ Value <<= Shift; /* 64 = 0100 0000 */ printf("%d\n", Value); /* Prints 64 */}
Please use unsigned variables with these operators to avoid unpredictable results.
Right Shift
The left operands value is moved right by the number of bits specified by the right operand.
#include <stdio.h> main() { unsigned int bytes=256; /* 00000000 00000000 00000000 10000000 */ do { printf("%3d \n", bytes); bytes >>= 1; /* 00000000 00000000 00000000 01000000 */ } while (bytes); return 0; }
O/P:
256 128 64 32 16 8 4 2 1
Ternary/Conditional Operator
The conditional expressions written with the ternary operator “?:” provides an alternate way to write the if conditional construct. This operator takes three arguments.
The syntax is:
expression1 ? expression2 : expression3
If expression1 is true (i.e. Value is non-zero), then the value returned would be expression2 otherwise the value returned would be expression3.
int num, res; scanf(“%d”, &num); res = ( num >= 0 ? 1 : 0 );
res contains 1 if num is positive or zero, else it contains 0.
int big, a, b, c; big = (a > b ? (a > c 3 : 4) : ( b > c ? 6 : 8 ));
big contains the highest of all the three numbers.
Compound Assignment operators
Most of the binary operators like +, * have a corresponding assignment operator of the form op= where op is one of +, -, *, /, %, &, |, ^. The explanation of these compound assignment operators is given below in the table 2.5.
Consider the value i = 15 for all the expressions given in the table below.
Comma Operator
The comma operator allows grouping two statements where one is expected.
Syntax:
assignment-expression ,assignment-expression
The comma operator has left-to-right associativity. Two expressions separated by a comma are evaluated left to right. The left operand is always evaluated, and all side effects are completed before the right operand is evaluated.
Consider the expression:
e1, e2
The type and value of the expression are the type and value of e2; the result of evaluating e1 is discarded. The result is an l-value if the right operand is an l-value.
This example illustrates the comma operator:
for ( i = j = 1; i + j < 20; i += i, j-- );
In this example, each operand of the for statement’s third expression is evaluated independently.The left operand i += i is evaluated first; then the right operand, j––, is evaluated.
Comma operator returns the value of the rightmost operand.
Usage of Comma operator:
#include<stdio.h> main() { int i, j; printf("%d",(i = 0, j = 10)); }
Output:
10
Usage of Comma operator:
#include<stdio.h> main(){ int i,j,k; k = (i = 4, j = 5); printf("k = %d",k); }
Output:
k = 5
Precedence and order of Evaluation
The hierarchy of commonly used operators is shown in the table below.
In case of a tie between operations of the same priority then they are evaluated based on their associativity. You can use parentheses to change the order of the evaluation. If there is more than one set of parentheses, the innermost parentheses will be performed first, followed by the operations within the second innermost pair, and so on.
C, like most languages, does not specify the order in which the operands of an operator are evaluated. Similarly, the order in which function arguments are evaluated is also not specified. So the statement
printf(“%d %d\n”, ++n, power(2, n)); /* AVOID */
can produce different results with different compilers, depending on whether n is incremented before power is called. The solution is to write:
++n; printf(“%d %d\n”, n, power(2, n));
Type Conversion
When an operator has operands of different types, they are converted to a common type according to a small number of rules. In general, the only automatic conversions are those that convert a “narrower” operand into a “wider” one without losing information, such as converting an integer to a floating-point value.
Implicit arithmetic conversions
If a binary operator like +, -, * or / that takes two operands of different types then the “lower” type is promoted to the “higher” type before the operation proceeds. The result is of the “higher” type.
An arithmetic operation between an integer and integer always yields an integer result. Operation between float and float always yields a float result. Operation between float and integer always yields a float result.
Type conversion in Assignments
In certain cases the type of the expression and the type of the variable on the left-hand side of assignment operator may not be same. In such a case the value of the expression promoted or demoted depending on the type of the variable on the left-hand side of = operator.
int p, iNum = 30; float b = 3.5; p = b; b = iNum;
In the above example, the first assignment will store 3 to the variable p, because p is an integer variable, it cannot store a float value. The float is demoted to an integer and its value is stored. Exactly opposite happens in the next statement. Here, 30 is promoted to 30.000000 and then stored in b, since b is a float variable.
Type casting
Explicit type conversions can be forced in any expression, with a unary operator called a cast. In the construction:
(type-name) expression
The expression is converted to the named type by the conversion rules. The precise meaning of a cast is as if the expression were assigned to a variable of the specified type, which is then used in place of the whole construction.
int iCount; float fVal = 34.8f; iCount = (int) fVal; /* iCount contains 34 */