Module 2. Basic components of ‘C’ programming language

 

Lesson 7

OPERATORS AND EXPRESSIONS – PART – I
(Arithmetic, Assignment and Relational Operators)

7.1  Arithmetic Operators

All the basic arithmetic operations such as addition, multiplication, division, subtraction and remainder of integer division calculation can be carried out in ‘C’. The arithmetic operators are given in Table -7.1.

Table 7.1 ‘C’ Arithmetic operators

Serial Number

Operator

Description

  1.  

+

Addition or Unary Plus

  1.  

-

Subtraction or Unary Minus

  1.  

*

Multiplication

  1.  

/

Division

  1.  

%

Modulus Operator (Remainder after integer division)

 

Note that there is no exponentiation operator in ‘C’. However, ‘C’ provides an inbuilt function, pow (see Table-14.1 for details) for the purpose. The operands connected through arithmetic operators must be numeric values, i.e., integer, floating-point or characters (recall that the character constants represent integer values as determined by the machine’s character set). The modulus operator expects both the operands to be integers as well as the second operand to be nonzero. Similarly, the division operator requires the second operand to be non-zero. Evidently, both unary as well as binary operators are supported by ‘C’. Unary operation is performed on a single operand, e.g., the unary minus (-) operator applied on the number 5 will produce the resultant value as -5. The operators follow certain precedence rules similarly as the rules of algebra in mathematics. For example, the following expressions demonstrate the use of various arithmetic operators:

Here a, b, c, x and y are known as operands. The modulus operator is a special operator in ‘C’, which evaluates the remainder of the operands after division.

7.2  Integer Arithmetic

When an arithmetic operation is performed on two whole numbers or integers then such an operation is called integer arithmetic. It always produces an integer (i.e., truncating the decimal portion of the quotient) as a result.

Illustration

Consider x = 27 and y = 5 be two integer numbers. Thus, the results of various arithmetic operations performed on these numbers are shown below.

Expression

Value

Expression

Value

Expression

Value

 

 

Note that the fractional part is truncated during integer division.

7.3  Floating-point Arithmetic

The process of applying an arithmetic operation on two real numbers is called floating-point arithmetic. The floating-point results can be truncated according to the properties requirement. Note that the modulus operator is not applicable to floating-point arithmetic operands.

Illustration

Consider x = 14.0 and y = 4.0 be two real numbers. Thus, the results of various arithmetic operations performed on these numbers are shown below.

Expression

Value

Expression

Value

Expression

Value

Expression

Value

 

Note that the interpretation of the remainder operation is ambiguous when one of the operands is negative. Most versions of ‘C’ assign the sign of the first operand to the remainder. Most versions of ‘C’ ascertain the sign of the remainder as a = ( (a/b) * b) + (a%b); however, this aspect is not mentioned in the formal definition of the language. Thus, the aforesaid condition will always be fulfilled regardless of the signs of the operands a and b. Novice programmers should be careful while using the modulus operator when one of the operands is negative.

Illustration

Consider a and b be two integer variables containing values 11 and -3, respectively. Several arithmetic expressions involving these variables and their resultant values are given below.

Expression

Value

Expression

Value

8

14

2

 

 

Operands with distinct data types commonly go through type conversion before the expression attains the final value. Generally, the final result is expressed in the highest precision possible, consistent with the data types of the operands. The following rules apply when neither operand is unsigned.

7.3.1  Conversion rules

These rules apply to arithmetic operations between two operators with dissimilar data types. There may be some variation from one version of ‘C’ to another.

1.      If one of the operands is long double, the other will be converted to long double and the result will be long double.

2.      Otherwise, if one of the operands is double, the other will be converted to double and the result will be double.

3.      Otherwise, if one of the operands is float, the other will be converted to float and the result will be float.

4.      Otherwise, if one of the operands is unsigned long int, the other will be converted to unsigned long int and the result will be unsigned long int.

5.      Otherwise, if one of the operands is long int and the other is unsigned int, then:

a.       If unsigned int can be converted to long int, the unsigned int operand will be converted as such and the result will be long int.

b.      Otherwise, both operands will be converted to unsigned long int and the result will be unsigned long int.

6.      Otherwise, if one of the operands is long int, the other will be converted to long int and the result will be long int.

7.      Otherwise, if one of the operands is unsigned int, the other will be converted to unsigned int and the result will be unsigned int.

8.      If none of the above conditions applies, then both operands will be converted to int (if necessary), and the result will be int.

Note that some versions of ‘C’ automatically convert all floating-point operands to double-precision.

Further, note that the ‘C’ operators are grouped hierarchically according to their precedence (i.e., the order of evaluation) as shown in Table-7.2.

Operations with higher precedence are evaluated prior to the lower precedence operations. However, the normal order of evaluation can be transformed by using parentheses, e.g., the expressions, a – b/c * d and (a – b)/(c * d) will produce different results. At times, it is a good practice to use parentheses to simplify an expression, even if the parentheses may not be required. On the other hand, very complex expressions should be avoided as these may be error-prone.      

Table 7.2 ‘C’ operators in order of precedence (highest to lowest) along with their associativity that indicates in what order operators of equal precedence in an expression are applied

Operator Group

Description

Associativity

()
[]
.
->
++  --

Parentheses (function call) (see Note 1)
Brackets (array subscript)
Member selection via object name
Member selection via pointer
Postfix increment/decrement (see Note 2)

Left-to-Right

++  --
+  -
!  ~
(type)
*
&
sizeof
  

Prefix increment/decrement
Unary plus/minus
Logical negation/bitwise complement
Cast (change type)
Dereference
Address
Determine size in bytes

Right-to-Left

*  /  %

Multiplication/division/modulus

Left-to-Right

+  -

Addition/subtraction

Left-to-Right

<<  >>

Bitwise shift left, Bitwise shift right

Left-to-Right

<  <=
>  >=

Relational less than/less than or equal to
Relational greater than/greater than or equal to

Left-to-Right

==  !=

Relational is equal to/is not equal to

Left-to-Right

&

Bitwise AND

Left-to-Right

^

Bitwise exclusive OR

Left-to-Right

|

Bitwise inclusive OR

Left-to-Right

&&

Logical AND

Left-to-Right

||

Logical OR

Left-to-Right

?:

Ternary conditional

Right-to-Left

=
+=  -=
*=  /=
%=  &=
^=  |=
<<=  >>=

Assignment
Addition/subtraction assignment
Multiplication/division assignment
Modulus/bitwise AND assignment
Bitwise exclusive/inclusive OR assignment
Bitwise shift left/right assignment

Right-to-Left

,

Comma (separate expressions)

Left-to-Right

Note 1:

Parentheses are also used to group sub-expressions to force a different precedence; such parenthetical expressions can be nested and are evaluated from inner to outer.

Note 2:

Postfix increment/decrement have higher precedence, but the actual increment or decrement of the operand is delayed (to be accomplished sometime before the statement completes execution). So in the statement y = x * z++; the current value of z is used to evaluate the expression (i.e., z++ evaluates to z) and z only incremented after all else is done.

7.4  Mixed-mode Arithmetic

The process of executing an arithmetic expression involving two different kinds of operand, i.e., a real operand and an integer operand is called mixed-mode arithmetic. In case one of the operands is of floating-point type, the result is always of the type, real. For instance, 15/10.0 = 1.5.

7.5  Assignment Operators

‘C’ assignment operators are used to assign the value of an expression to an identifier. The syntax of assignment operators is:

               

Examples: Following typical assignment expressions depict use of the commonly used assignment operator, ‘=’.

            (i)           a = 3                                     (ii)          delta = 0.001                                    (iii)         area = length * breadth

The first assignment expression causes the integer value 3 to be assigned to the variable a, and the second assignment causes the floating-point value 0.001 to the variable delta. The third assignment causes the value of the RHS arithmetic expression being assigned to the Left Hand Side (LHS) variable, i.e., the value of the expression length * breadth is assigned to the variable area.

Note: The assignment operator ‘=’ and the equality operator ‘==’ are distinct. Hence, these operators are used for their specific purpose only and are not interchangeable. If the two operands in an assignment expression are of different data types, then the value of the expression on RHS will automatically be converted to the type of the identifier on LHS. In some cases, this automatic type conversion can cause an alteration of the data being assigned. Some instances for better comprehension of the students are:

·         A floating-point value may be truncated if assigned to an integer identifier

·         A double-precision value may be rounded if assigned to a single-precision floating-point identifier

·         An integer quantity may be changed if assigned to a shorter integer identifier or a character identifier, i.e., some high-order bits are lost

·         The value of a character constant assigned to a numeric-type identifier will be dependent upon the particular character set in use, which may cause inconsistencies from one version of ‘C’ to another version.

Hence, the careless use of type conversions is a frequent source of error among the beginners.

Examples

Consider i as an integer variable.

Assignment Expression

Resultant Value

Now, consider two integer variables, i and j; contains a value of 5. Several assignment expressions that make use of these variables are given below for illustration:

Assignment Expression

Resultant Value

Further, assume that i is an integer variable and ASCII character set is applicable.

Assignment Expression

Resultant Value

‘C’ supports multiple assignments as follows:

These assignment operations are performed from right to left. Thus, the aforementioned multiple assignment is equivalent to:

               

That is, right to left nesting of multiple assignments is possible.

An illustration

Let i and j are two integer variables. The multiple assignment expression

               

will cause the value 3 to be assigned to both i and j. To be more precise, the value 3 is first assigned to the variable j followed by the assignment to i. Similarly, the multiple assignment expression

               

assigns the integer value 3 to both the variables, i and j.    

Beside ‘=’ operator, ‘C’ supports five more assignment operators such as +=, -=, *=, /= and %=. All the five operators are discussed here. Consider the operator +=. The assignment expression

is equivalent to        

               

Similarly,

               

is equivalent to

               

and so on for all these five assignment operators. Note that expression 1 is usually a variable or an array element.

Example

Consider i and j as integer variables containing values 5 and 7, respectively; and f and g are floating-point variables having values 5.5 and -3.25, respectively. Several assignment expressions making use of these variables are shown below for illustration. Each expression utilises the values of the variables, i, j, f and g.

Expression

Equivalent Expression

Final Value

Each assignment operator has a priority and they are evaluated from right to left based on its priority. The priority of assignment operators is: +=, -=, *=, /= and %=. Assignment operators have right to left associativity. The unary operations, arithmetic operations, relational and equality operations and logical operations are all carried out before assignment operations.

Example

Consider that x, y and z are integer variables, which have been assigned the value 2, 3 and 4, respectively. The expression

               

This expression assigns the value of -8 to the variable, x.

It is interesting to note the order in which the expression is evaluated. You know that the arithmetic operations precede the assignment operation. Therefore, the expression (y+z) will be evaluated first of all thereby producing the value, 7; then this value is multiplied by -2, yielding -14; and this value is further divided by 3 and truncated due to integer division, resulting in -4. Finally, this truncated quotient is multiplied by the original value of x (i.e., 2) to yield the final result of -8.

7.6  Relational Operators

Relational operators are used to compare two values. These operators are used in Boolean conditions or expressions called logical expressions. The resulting expressions will be of the integer type as TRUE is represented by the integer value 1and FLASE is represented by the value 0. The relational operators supported by ‘C’ are given in Table-7.3.

Table 7.3 ‘C’ Relational operators 

Serial Number

Operator

Description

Relational Operators

  1.  

<

Less than

  1.  

<=

Less than or equal to

  1.  

>

Greater than

  1.  

>=

Greater than or equal to

Equality Operators

  1.  

==

Equal to

  1.  

!=

Not equal to

The relational operators as shown at serial numbers, 1 to 4 under Table-7.3, fall within the same precedence group and this is lower than the arithmetic as well as unary operators. The associativity of these operators is left to right. There are two equality operators as mentioned at serial numbers 5 and 6 under Table-7.3, which are closely associated with the relational operators. The equality operators fall into a separate precedence group beneath the relational operators. The associativity of these operators is also left to right. 

An illustration

Consider that it is required to compare, for equality, the marks obtained by two students in a course on Computer Programming. Suppose, the marks obtained by the first student (denoted by variable, student_1) are 50; and that of the second student (denoted by variable, student_2) are 47. Now, it is to compare the two for equality. This can be realised by using an appropriate relational operator as follows:

student_1==student_2

Obviously, the resultant will be FALSE with resultant value as 0.                                                                     

Example

Suppose i, j and k are integer variables containing values 1, 2 and 3, respectively. Several logical expressions involving these variables are given below:

Expression

Description

Resultant Value

true

1

true

1

false

0

false

0

true

1

While carrying out relational and equality operations, operands that differ in type will be converted in accordance with the conversion rules discussed earlier (see Sub-section 7.3.1).

Example

Suppose that i i s an integer variable containing value as 7; f is a floating-point variable having value a 5.5; and c is a character type variable that represents the character ‘w’. Several logical expressions showing use of these variables are given below. Each expression comprises of two different type operands. Also, it is assumed that ASCII character set is applicable.

Expression

Description

Resultant Value

true

1

false

0

true

1

true

1

false

0

7.7  Structure of a Typical ‘C’ Programme

Every ‘C’ programme consists of one or more modules called functions. One of the functions must be named as main. The programme will always begin with executing the main function, which may access other functions. Any other function definitions must be defined separately; either ahead of or after the main function (refer to the Module-IV for further details on functions). Each function must contain a function heading, which consists of the function name followed by an optional list of arguments enclosed in parentheses, a list of argument declarations if arguments are included in the heading and a compound statement, which comprises the remainder of the function.

An illustration

A typical ‘C’ programme demonstrating various ‘C’ operators is given below.

/* A programme demonstrating C arithmetic operators */

#include <stdio.h>

 

void  main()

{

int x = 10, y = 20;  

printf("x = %d\n",x);  

printf("y = %d\n",y);

/* demonstrates = and + operators */ 

y = y + x; 

printf("y = y + x; y = %d\n",y);  

/* demonstrates - operator */ 

y = y - 2;

printf("y = y - 2; y = %d\n",y);  

/* demonstrates * operator */

y = y * 5;  

printf("y = y * 5; y = %d\n",y);  

/* demonstrate / operator */

y = y / 5;  

printf("y = y / 5; y = %d\n",y);  

/* demonstrates modulus operator % */

int remainder = 0;  

remainder = y %3;  

printf("remainder = y %% 3; remainder = %d\n",remainder);  

/* keeps console screen until a key stroke */

char key;  

scanf(&key);

}

Output produced by the above programme

x = 10
y = 20
y = y + x; y = 30
y = y - 2; y = 28
y = y * 5; y = 140
y = y / 5; y = 28
remainder = y % 3; remainder = 1

7.8  Types of Programme Error

There are three basic types of programme error, viz., syntax error; semantic error and logical error. Programme errors are also referred to as programme bugs. The term ‘bug’ was so coined that one of the earliest programme errors ever detected involved a moth flying into the computer causing short-circuiting of the electronics. Hence, the process of removing programme errors is called debugging. While that goal is laudable, every programmer, no matter how seasoned, writes programmes that contain errors. However, a skilled programmer can detect, isolate and correct programme errors more quickly than a less skilled programmer. Also, experienced programmers do make fewer programming errors simply because of their experience! The lesson here is that you should expect to make a lot of programme errors in the beginning as every beginner does.

View each programme error as a challenge and learn from the experience. Let us take a quick overview of the three types of programme error.

Syntax Errors

The first type of error is a syntax error. You already know that syntax errors are caused when you don’t obey the syntax rules. A common syntax rule you might make in the beginning is forgetting to terminate each programme statement with a semicolon.

Logic Errors

Logic errors are those errors that remain after all the semantic and syntax errors have been removed. Usually, logic errors manifest themselves when the result the programme produces doesn’t match the result your test data suggest it should produce. Generlly, logic errors are found in the Process. Logic errors occur when you implement the algorithm for solving the problem incorrectly.

The key to fixing logic errors is to be able to reproduce the error consistently. A repeatable logic error is much easier to track down and fix than an error that appears to be occurring randomly.

Semantic Errors

A semantic error occurs when you obey the syntax rules of the language but are using the statement out of context. For example, a sentence in English is expected to have a noun and a verb. Consider the sentence “The dog meowed”. This sentence does obey the rules of having a noun and a verb, but the context of the sentence is out of whack. Dogs don’t meow; therefore, the context of the statement is incorrect. The error message, I showed you earlier: the name i does not exist in the current context refers to a type of semantic error. There may well be a variable named i defined somewhere in the programme, but it is not currently in scope. That is, you are trying to use i when it is out of scope.