Operators are the symbols, which are used to perform operations on the operands. Operators are always associated with operands.

Based on the operations they perform, operators are classified into different types :

  • Arithmetic operators
  • Assignment operators
  • Relational operators
  • Logical operators
  • Increment and decrement operators
  • Bit-wise operators
  • Ternary operator
  • Special operators

What is an operand?

An operand is data used in the program, It may be a constant literal or an object that holds a value.

What is an expression?

An expression contains both operators and operands.

Example: a = b + c;

We can say, both "b + c" and "a = b + c" are expressions. Every expression will produce a result that can be assigned to a new operand.

In the above expression, we are performing the addition of b & c which will later be assigned to 'a'.

  • '+ =' are operators.
  • a, b, c are the operands.
  • a = (b + c) is an expression.

Arithmetic operators

Following are the arithmetic operators:

  • Addition (+)
  • Subtraction (-)
  • Multiplication (*)
  • Division (/)
  • Modulo (%)

Below is the example program with addition (+), subtraction (-), multiplication (*), modulus(%) operators.

  • '%' modulo operator is used to find the reminder of two operands.
  • Modulus operator should not be used between a float or double operands.

Example program on arithmetic operators in C

#include <stdio.h>
int main(){
    int a = 10, b = 20;
    int result = 0;
    result = a + b;
    printf("a + b = %d\n", result);
    result = a - b;
    printf("a - b = %d\n", result);
    result = a c* b;
    printf("a * b = %d\n", result);
    result = b % a;
    printf("b %% a = %d\n", result);
    return 0;
}

Output

a + b = 30
a - b = -10
a * b = 200
b % a = 0

Below is the example program with the division (/) operator.

Note: When we use a division (/) operator we might get the decimal point value as a result. So we need to use float or double data type to store the result else the value will be rounded to the nearest least decimal value i.e fraction part will be truncated.

For negative operands, it is machine-dependent. The value will either underflow or overflow.

Example program on division operator with float and double datatype

#include <stdio.h>
int main(){
    float a = 2, b = 3;
    float result = b/a;
    printf("The result using float is : %f\n", result );
    int c = 2, d = 3;
    int result2 = b/a;
    printf("The result using int is : %d\n", result2);
    return 0;
}

Output

The result using float is: 1.500000
The result using int is: 1

Task-to-do:

  1. Check, whether is it possible to use operators on operands of different data types.
  2. Check, whether we can able to perform modulus operations for float or double operands.

Assignment operators

Assignment operators are used to performing operations such as

  • Assign (=)
  • Assignment after arithmetic operation (+=, -=, *=, /=, %=)
  • Assignment after the bitwise operation (<<=, >>=, &=, |=, ^=)

The assignment operators are used to assign the R-value to L-value. For all the assignment operators, there will be an L-Value and R-Value. Please refer to this article for more information on L-value and R-value.

Example program with the assignment (=) operator.

#include <stdio.h>
int main() 
{
    int a = 0;

    /* 'a' is said be an L_value*/
    /* 'a+10' is an R_value */
    /* L_value = R_value */

    a = a +10;
    printf("The L-value is : %d", a)    

    return 0;
}

Explanation:

In the above program, we are assigning or copying the result of a+10 to the variable a by using the assignment(=) operator.

To simplify the expressions, we use the arithmetic and bit-wise operators along with the assignment operators. Let's see the below example program on simplifying the above expression a = a +10.

Program using arithmetic, bit-wise with an assignment operator.

#include <stdio.h>
int main(){
    int a = 10;
    a += 10;
    printf("a = a + 10 : %d \n", a);
    a -= 10;
    printf("a = a - 10 : %d \n", a);
    a *= 10;
    printf("a = a * 10 : %d \n", a);
    a /= 10;
    printf("a = a / 10 : %d \n", a);
    a %= 10;
    printf("a = a % 10 : %d \n", a);
    return 0;
}

Explanation:

In the above program, both 'a +=10' and 'a = a + 10' are same and will produce the same result. We can also use, Bitwise AND (&), OR (|), XOR (^) with the assignment (=) operator.

Example: &=, |=, ^=

References:

Chapter 2 - Section 2.10 "Assignment Operators" in the book "The C programming language 2nd edition" written by Brian Kernighan and Dennis M Ritchie.

Relational operators

Relational operators are used to performing operations such as

  • Less than (<)
  • Less than or equal to (<=)
  • Greater than (>)
  • Greater than or equal to (>=)
  • Equal to (==)
  • Not equal to (!=)

Syntax: Operand operator Operand

Relational operators are used to checking the relation between operands. If the given relation matches then the output will be True i.e. 1, else the output is False i.e. 0.

Example program on relational operators

#include <stdio.h>
int main(){
	printf("10 is less than 10:  %d\n", 10 < 10);
	printf("10 is less than or equal to 10:  %d\n", 10 <= 10);
	printf("10 is greater than 10:  %d\n", 10 > 10);
	printf("10 is greater than or equal to 10:  %d\n", 10 >= 10);
	printf("10 is not equal to 10: %d\n", 10 != 10);
	printf("10 is equal to 10: %d\n", 10 == 10);
    return 0;
}

Note:  Always remember, the result of relational operators will be either 0 or 1. If the relation matches then the output is 1 else it is 0.

Output:

10 is less than 10:  0
10 is less than or equal to 10:  1
10 is greater than 10:  0
10 is greater than or equal to 10:  1
10 is not equal to 10: 0
10 is equal to 10: 1

We will see more about relational operators in the article flow control statements.


Logical operators

Logical operators are used to performing operations such as

  • Logical AND (&&)
  • Logical OR (||)
  • Logical NOT (!)

Logical (&, ||) operators are used between two operands and the result will be 0 or 1 as per the below combinations. NOT operator should be used with a single operand and the result will be based on the below combinations.

Note: In the given table, the non_zero value means values other than zero including negative values.  i.e. (... -3, -2, -1, 1, 2, 3 ...)

Combinations with logical AND operator:

1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0

Explanation:

As I said earlier,  we can also call 'non_zero value' as true and '0' as false. So if both the conditions are true then the result is true else false.

Combinations with logical OR operator:

1 || 1 = 1
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0

Combinations with logical NOT operator:

!1 = 0
!0 = 1

An example C program using logical AND, OR, NOT operator.

#include <stdio.h>
int main()
{
	/* Logical AND(&&) operators */
	printf("1 && 1 = %d\n", 1 && 1);
	printf("1 && 0 = %d\n", 1 && 0);
	printf("0 && 1 = %d\n", 0 && 1);
	printf("0 && 0 = %d\n\n", 0 && 0);
	
	/* Logical OR(||) operators */
	printf("1 || 1 = %d\n", 1 || 1);
	printf("1 || 0 = %d\n", 1 || 0);
	printf("0 || 1 = %d\n", 0 || 1);
	printf("0 || 0 = %d\n\n", 0 || 0);
	
	/* Logical NOT(!) operators */
	printf("!1 = %d\n", !1);
	printf("!0 = %d\n", !0);	
	
}

Output:

1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0

1 || 1 = 1
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0

!1 = 0
!0 = 1

Note: We'll get to know the real-time use of logical operators in the chapter "control flow statements".


Increment and decrement operators

Increment and decrement operators are used to perform operations such as

  • Increment (++) - Increment the value by 1
  • Decrement (--) - Decrement the value by 1

Increment operator (++):

The increment operator is categorized into two types i.e. PRE-Increment and POST -Increment.

  • In pre-increment, after incrementing the value by 1, then the expression is evaluated.
  • In post-increment, after evaluating the expression the value will be incremented by 1.

Example program with pre-increment and post-increment operators

#include <stdio.h>
int main()
{
    int a = 0;
    printf("%d\n", ++a);
    printf("%d\n", a++);
    printf("%d\n", a);
    return 0;
}

Output:

1
1
2

Explanation:

In the above code, ++operand will increment the value first and later proceed for printing the value. whereas in operand++ after printing then the value is incremented.

Decrement operator (--):

Decrement operator is categorized into two types i.e. PRE-Decrement and POST-Decrement.

  • In pre-decrement, after decrementing the value by 1, then the expression is evaluated.
  • In post-decrement, after evaluating the expression the value will be decremented by 1.

Example program with pre-decrement and post-decrement operators

#include <stdio.h>
int main()
{
    int a = 2;
    printf("%d\n", --a);
    printf("%d\n", a--);
    printf("%d\n", a);
    return 0;
}
1
1
0

Explanation:

In the above code, the --operand will decrement the value by 1 and then print it. whereas, in operand-- after printing the value then it is decremented by 1.


Bitwise operators

C provides six Bitwise operators for bit manipulation. They are

  • Bitwise AND (&)
  • Bitwise OR (|)
  • Bitwise XOR (^)
  • Left shift (<<)
  • Right shift (>>)
  • One's complement (~)

Note: Bitwise operators are applicable only to the Integral datatypes i.e. char, short, int, long.

In a little-endian machine, the information is stored from LSB(Least significant bit) to MSB(Most significant bit) i.e. from right to left. Let's say, if the value is 10 then the equivalent binary value is stored as below.

Bitwise AND(&), OR(|), XOR(^):

Bitwise AND, OR, XOR operators will be used between two operands of Integral types and give the result as per the below-given combinations.

Bitwise AND:

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

Example: 10 & 4 = 0

i.e. the equivalent binary value for 10 is '1010' & 4 is 0100.

Bitwise OR:

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

Example :  10 | 4 = 14

i.e. the equivalent binary value for 10 is '1010'  and  4 is 0100.

Bitwise XOR:

1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

Example:  10 ^ 4 = 14

i.e. the equivalent binary value for 10 is '1010' & 4 is 0100.

Remember, when we are performing the bitwise operation, the operation will be performed on entire bits of the given operand i.e. if we apply the bitwise on int type operand,  then the operation will be performed on all the 32-bits. In the above example, I am assuming the remaining bits are set to 0. So I was just explaining with 4 bits.

One's complement (~):

One's complement operator is also known as a unary operator because it is applicable only to a single operand of the Integral type. When we apply one's complement to value, all 0's are flipped to 1's and vice versa.

Example:  ~2 = -3

i.e. the equivalent binary value of 2 is 0010

As you have seen the above result,  i.e. 111 .... 11101, The most significant bit is set to 1, so the value is treated as a negative value and the result is printed after calculating the two's complement of that value. To find the one's complement of a given number we can use formulae   -(n+1).

Note: Please learn the binary representation of the negative number.

Example C program on Bitwise AND, OR, NOT & 1's complement.

#include <stdio.h>
int main()
{
	int operand1 = 10;
	int operand2 = 4;
	printf("The result after the bitwise AND is : %d\n", operand1 & operand2);
	printf("The result after the bitwise OR is : %d\n", operand1 | operand2);
	printf("The result after the bitwise XOR is : %d\n", operand1 ^ operand2);
	printf("The result after the One's complement is : %d\n", ~(2));
	return 0;
}

Output

The result after the bitwise AND is : 0
The result after the bitwise OR is: 14
The result after the bitwise XOR is: 14
The result after the One's complement is: -3

Left shift (<<) :

The left shift operation is used to left-shift the bits with a given number of times.

Tip: The result of the left shift operation will be calculated using the formulae: value x 2 powerof (no_of_shifts).

Right shift(>>):

The right shift operation will shift all the bits to the right side with the given no of times.

Tip: The result of the right shift operation will be calculated using the formulae: value / 2 (powerof (no_of_shifts))

Example C program with left (<<) and right shift (>>) operators.

#include <stdio.h>
int main()
{
	int operand1 = 2;
	int operand2 = 15;
	printf("The result after the Left shift is : %d\n", operand1 << 3);
	printf("The result after the Right shift is : %d\n", operand2 >> 2 );
	return 0;
}

Output:

The result after the Left shift is : 16
The result after the Right shift is : 3

Ternary Operator

The ternary operator is used to writing the conditional expression and the evaluation will be done as per the given condition.

Syntax:   expr1 ? expr2 : expr3

Example:    z = (a > b) ? a  is big : b is big;

  • If the result of expr1 is '1 or non-zero value',  then expr2 is evaluated.
  • If the result of expr1 is '0' then expr3 will be evaluated.

As we already know how relational operators work, we can say that from the above expression, if a>b,  then the result is '1' else the result is 0. So, If the result is 1 then the expression 'a is big'  is evaluated else the expression 'b is big' is evaluated.

Example c program with the ternary operator

#include <stdio.h>
int main(void) 
{
	int a = 10;
	int b = 20;
	printf("%s", (a>b)?"a is big":"b is big");
	return 0;
}

Output:

b is big

Task-to-do:

  1. Write a C program to check whether the number is even or odd using the ternary operator.

Special operators

Apart from the operators that we have seen in the earlier articles, C also has few special operators that are required to unveil C's true power. They are

  • Address operator (&)
  • Reference operators (->, *)
  • sizeof operator

Address Operator (Unary &)

In the bitwise operator's article, we saw that '&' is a Bitwise AND operator and used to perform AND operation.

Remember, if & operator is used in between two operands then it is a bitwise AND operator else it is an Address operator or Unary & operator.

Address operator is used to getting the address of an operand. This operator should be only used on Lvalue operands (excluding bitfields & register variables) because Rvalue operands don't have an address space associated with them.

Example

#include <stdio.h>

int main(){
   int a = 10;
   printf("%d", &a);
}

In the above example, we are getting the address of a variable using the & operator.

Reference operators:

It is recommended to discuss these operators in the pointers article. But for now, we discuss them in brief.

  • "->" & "*" are used to de-reference an address location. In Lehman terms, "to access the value in some address location".
  • * operator is used to accessing the value in the address pointed by a pointer variable.
  • -> is particularly used to de-reference & access the members in a structure and union. Instead of "->", we can also use "*" and "." to de-reference and access the structure or union. But it is a two-step process. So to simplify we use ->. We will discuss more on this in the structure and union article.

Example-1:

#include <stdio.h>

int main(){
   int *p;
   int a = 10;
   p = &a;
   printf("%d", *p); // using * to de-reference p.
}

Example-2:

#include <stdio.h>

struct A {
   int a;
};

int main(){
   struct A instance = {10};
   struct A *pinstance = &instance; // Created a pointer instance for structure
   
   // De-reference using ->
   printf("%d\n", pinstance -> a);

   // De-reference using * and .
   printf("%d\n", (*pinstance).a); 

   return 0;
}

sizeof operator:

The sizeof operator is used to finding the no of bytes required to store the object of its type.

Example:

#include <stdio.h>

int main() {

   int a = 10;
   
   printf("%d \n", sizeof a);
   printf("%d \n", sizeof(int));
   printf("%d \n", sizeof 10);
   
   return 0

}
  • In the above example, we calculated the sizeof of a Constant, Object and Type. In all the cases we got 4 as a result because "a" & "10" is associated with type int.
  • When we apply sizeof to the complete array, the result will be the total size of an array (T) i.e. T = no of elements X size of an array type.
  • When applied to structure the result is the sum of the size of each member type. In the case of a union, it is the size of the maximum member type.

Example:

#include <stdio.h>

struct A {
   int a;
   int b;
   int c;
};


union B {
   int a;
   int b;
   int c;
};

int main(){
  
  struct A sinstance;
  union B uinstance; 
  int a[10];

  printf("%lu\n", sizeof sinstance);
  printf("%lu\n", sizeof uinstance);
  printf("%lu\n", sizeof a);
   return 0;
}

Note: Return type of sizeof is unsigned long int or size_t.

References:

Appendix A, Section A.7.4.2 Address operator, Section A.7.3.3 structure references, Section A.7.4.8 Sizeof Operator in the book "The C programming language by Dennis Ritchie".