C Operators and Type Conversion

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.

Note: All the expressions, which are part of a compound expression, may not be evaluated, when they are connected by && or || operators.

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 */ 
Related Post