0% found this document useful (0 votes)
18 views65 pages

ITP Week 2

This document provides an introduction to writing a basic C program, including essential elements such as preprocessor directives, functions, and comments. It explains how to compile and run a C program, as well as how to handle errors during compilation and execution. Additionally, it covers variables, constants, and data types in C, detailing memory allocation and representation of data in programs.

Uploaded by

asfcsharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views65 pages

ITP Week 2

This document provides an introduction to writing a basic C program, including essential elements such as preprocessor directives, functions, and comments. It explains how to compile and run a C program, as well as how to handle errors during compilation and execution. Additionally, it covers variables, constants, and data types in C, detailing memory allocation and representation of data in programs.

Uploaded by

asfcsharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 65

Reading: Basic C Program

Reading Objective

In this reading, you will learn to write a C program. You will gain insights into the essential elements of a C program. You
will then learn how to compile the program using a compiler on Coursera Labs and execute it on the processor.

How to Write a C Program?

First, we must understand the various elements essential to writing a C program. Then we need to define each step the
program will take before we write the actual code.

Let’s get started with our first C program. We will write a program to find out the sum of two numbers.

The program steps are as follows:

1. START
2. Input the numbers num1, num2
3. Initialize sum = 0
4. Set sum = num1 + num2
5. Print sum
6. END

Figure 1: Program to print the sum of two numbers

Fig. 1 shows an example program to determine the sum of two numbers. Let us understand the individual elements of the
program.

a. Preprocessor directive

In Fig. 1, we see the second line #include<stdio.h> that instructs the preprocessor to insert the contents of the file stdio.h
at the beginning of the program. We must know that a file with a .h extension is a header file that informs the C compiler
about standard pre-written C functions that other users can use in their program. Now, stdio.h is a header file that
contains information about functions in the C programming language to perform input and output operations, where
stdio means Standard Input Output. This file is stored in a standard directory where the compiler stores all the header
files.

b. Comments

In Fig. 1, we can see a lot of texts written in green color. These are comments. Comments are statements written in
natural language, usually English, to enhance the readability of our code for the programmer and others. Programmers
use these comments to explain the logic they implement in a particular line or function. Comments can be employed to
convey related information to the human reader of the program to help them understand the code better. There are two
ways we can create comments in C. First, we can write a // to make a single-line comment extending until the end of the
current line. And second, we can write any text between /* and */, which is considered a multi-line comment.

/* This is a multi-line

comment.*/

It is worth noting that a multi-line comment cannot be nested.

c. Functions

There are some tasks that we want to perform repeatedly in a program. For this purpose, we can use lines 7 and 8 in Fig.
1, which are called the printf( ) and the scanf( ) functions, respectively. We can easily observe the parenthesis indicating
printf and scanf names of functions in C.

• printf() function allows us to print something on the screen


• scanf() function allows us to take input from the user into a C program
We can call a function by its name to execute a predefined task/functionality. We can also pass some parameters to a
function to instruct it on how to perform the task. For example, we can write a function that calculates the square of a
number and passes the number to the function as a parameter. Once we have written a function, multiple programs can
use it to execute the same function.

We can also look at the compilation, another commonly used terminology in computer programming. The compilation
converts high-level code written in a particular programming language into low-level machine code understandable by
the computer.

d. Handling errors

We can encounter some problems called errors during the compilation or execution of a computer program. Errors can
also occur after successful compilation and execution, but with incorrect output. There are three types of errors: compile-
time errors, run-time errors, and semantic errors.

• Compile-time errors occur when some statements in our code do not follow the rules of the programming
language in which they are written. These errors are raised by the compiler while trying to compile our code.
For example, in Fig. 1, we can see that all the statements between lines 6 to 11 in the C program end in a semicolon. If we
forget to put a semicolon, it raises a compile-time error. int a, b, sum;

• Run-time errors occur during the execution of our program but are not detected during compilation.
For example, a statement a = b / c; can cause an error if the value of c is zero.

• Semantic errors occur when the code compiles and executes successfully but does not perform the intended
task.
For example, if we use == instead of using =, the meaning of the expression will change completely. However, the program
will still compile and execute properly. a = b means the value of b is assigned to a, whereas a == b is a Boolean expression
whose value is True if values of a and b are the same, and False otherwise.

How to Compile and Run a C Program?

Let us now look at compiling and running a C program. Fig. 2 shows the steps involved in running a C program. It shows
how the operating system loads the program into RAM and then executes it line by line on the CPU.
Steps to run a C program

• Firstly, we write a C program in any file with the extension .c and store it on disk. The .c extension denotes files
written in the C programming language.
• Then, we compile our program and generate an executable file using the gcc <filename>.c command. For
example, gccmyFirst.c
• The above compilation generates an executable file named a.out and stores it on disk.
• Now, we can execute our program by running the ./a.out command.
• When we run the above command, the OS loads the executable file onto the RAM and instructs the CPU to
execute it line by line.
Reading Summary:

In this reading, you have learned the following:

• Essential elements of a C program, like preprocessor directives, functions, and comments


• How to write your first C program
• Compiling and handling some basic kinds of errors in C programs
• Running the commands and executing a C program
Reading: Variables, Constants, and Datatypes
Reading Objective

In this reading, you will be introduced to variables, constants, and datatypes in C. You will understand how memory
space is allocated to variables in RAM. You will also understand how characters are internally represented as integers and
how implicit and explicit type conversions are performed in C.

Data in Computer Programs

We write computer programs to solve real-world problems. For this, programs must be very efficient in handling real-
world data. Programming languages have come up with different data types that support different kinds of data to
address the need to represent real-world data in computer programs.

Data in a program can be stored as a constant or a variable. A constant is a data item whose value remains fixed
throughout the entire program. A variable is a data item whose value can be changed during the program’s execution.
Variables are allocated a fixed memory location in the RAM from where we can read and modify the variable. For example,
if we need to store the value of π in a program, we will use a constant. To iterate over a list of 20,000 items, we will use a
variable to indicate the current position that we are reading and keep incrementing the value of that variable to iterate
through the entire list.

Variables in C Programming Language

We can declare a variable by specifying the data type of the variable followed by the variable name. For example, int age,
or float avg.

• Declaration of a variable allocates memory to that variable on the main memory.


• We can also declare more than one variable of a data type simultaneously by separating the variable names with
a comma separator. For example, int age, marks.
• Along with the declaration of the variables, we can also initialize them to some specific values. For example, int
age = 20, marks = 80. If the variable is not initialized during declaration, it contains a junk value.
• Let us consider the statement: int age = 20, marks = 80. The above statement declares two integer data type
variables with age and marks as the variable names and also initializes them to the values 20 and 80,
respectively. Since we have created two variables, we can change their values later in the program as follows: age
= 22, marks = 70
• Every variable must be declared before it is used. Otherwise, the compiler would raise an error.
• Certain rules to be followed while giving names to variables:
o Every variable name should start with an alphabet or an underscore and can contain alphabets,
numbers, and underscores only.
o Variable names cannot be a keyword. Keywords in C are some reserved words for special purposes. Some
examples of keywords are int, main, return, and struct.
o Variable names are case-sensitive, that is, Number and number are two different variable names.
o Preferably, variable names should be meaningful, denoting the value the variable will store. For example,
age and number.
• C is a typed programming language, meaning all the variables in C have a specific data type. Once we have
declared the data type of a variable, it cannot be changed.
• Fundamental data types in C are as follows:
o Integer (short, int, long, long long)
o Floating point (float, double, long double)
o Character (char)
• Some examples of variable declaration in different data types:
o int age = 21
o float pi = 3.14
o char ch = ‘S’
o int amount1 = 45, amount2 = 500, amount3 = 999
• Every datatype is associated with a fixed amount of storage.
Constants in C Programming Language

A constant is a data item whose value remains fixed throughout the entire program. C specifies a default datatype with
every constant. For example, 25, 6.25, and ‘$’ are all constants.

• The default data type of an integer in C is int. If the constant does not fit into an integer, a larger size data type is
used to store the constant.
• The default data type for a floating point number is double.
• We can use suffixes to assign data types to constants other than their default datatypes. For integer constants,
the suffix U or you can be used to make them unsigned integers, suffix L or l can be used to make them as long
integers. When a double constant is suffixed by f or F, it demotes the constant to a float datatype, and the suffix L
or l promotes it to a long double datatype.
• There are two ways of defining constants in C.
o Using a const qualifier In a variable declaration, we can add const qualifier in front of the declaration
to tell the compiler that this variable declaration is a constant declaration, not a variable declaration. For
example, const float pi = 3.14f; // here pi is a read-only value. Also, note that we have used ‘f’ as a suffix to
indicate that the constant value 3.14 is to be interpreted as a float value, not a double value.
o Using symbolic constants #define We can use preprocessor directives to define constants in our code.
For example, #define PI 3.14f // no semicolon used Before compiling the code, the compiler will replace
all the occurrences of PI in our program with 3.14f. Note: It is good practice to use all capital letters when
defining #define constants and to start all variables with a small letter.
o The difference between the two types of constants above is that in the first type, we have declared a
constant that takes actual memory on RAM. Still, because we have specified the const qualifier in the
declaration, the compiler won’t allow the value of this variable to be changed during the execution of the
program. Whereas when we use #define, no variable is declared. The compiler is just replacing PI with
3.14f in our program before it starts compilation

Where and How is the Program Data Stored?

Figure 1: C program showing variable declaration.

Consider the C program shown in Figure 1 above. Its main function has three variables declared: num1, num2, and num3.
Where do these three variables get stored in the main memory?
Figure 2: Steps of program execution.

We have noted that the compilation of a C program creates an executable stored on the hard disk. When this executable
file is executed, the operating system (OS) loads our C program into the RAM and executes it line by line on the CPU. Let
us look closely at the memory allocated to the program in the main memory.

Figure 3: Memory allocation to a C program on RAM.

When the OS loads the program into RAM, it allocates some memory to the program. As shown in Figure 3, the variables
named in the program are stored in this space.

Variable Types and Their Storage

Variables in a C program occupy a fixed amount of RAM space on the RAM depending on their datatype. For example, an
int variable will occupy 2 or 4 bytes depending on the compiler used to compile the program. Main memory is logically
represented as a massive table of rows, where each row can store 8 bits. A bit stores a binary value of 0 or 1, sometimes
called True or False. A set of 8 contiguous bits is known as a byte. Figure 4 shows a snapshot of how the RAM looks when
an int variable is declared and initialized:

int num = 4;

Four bytes are allocated, as shown in addresses from 1004 through 1007, and the value of 4 is in these four bytes.
Figure 5 shows different integer types supported by the C programming language and what the range of values that these
datatypes can represent. Since any variable in C takes a finite amount of memory, it can be easily understood that there
will be a certain limit on the number of values that it can represent. The precise size of all the datatypes is not explicitly
defined in the C language definition. It is left up to the compiler to decide how much memory to allocate to those
datatypes. However, we can still observe that allocated memory follows the order below: short, unsigned short <= int,
unsigned int <= long, unsigned long <= long long, unsigned long long.

It is also very easy to determine the number of bytes allotted to a particular variable. The sizeof() operator returns the
number of bytes allotted to a variable. Its return type is unsigned long int. Figure 6 shows an example of a C program that
computes the amount of memory allocated for variables of different datatypes and its output on execution. Please go to
Coursera Labs and try to execute this code. You learn better when you execute the code yourself.

Figure 4: Visualization of some memory locations in RAM when a variable is declared and initialized.

Figure 5: Integer datatypes supported by C programming language ds.


Figure 6: Example C program to show the working of sizeof() operator.

Figure 7 shows the different types of floating-point datatypes supported by C, the number of bytes used by them, and
their range in the form of mantissa and exponent. Please note that a double has a higher precision than a float in terms of
the number of digits.

Figure 7: Types of Floating-Point Numbers in C.

Character Types in C:

We have heard about representing integers in binary format, but what about characters? How do computers represent
characters in binary format? Computers use ASCII (American Standard Code for Information Interchange) character-
encoding scheme. ASCII defines an encoding to represent English characters (a-z, A-z) along with the other symbols (!, @,
#, $), punctuation marks (:, “, ‘, ?, /), numeric characters (0-9) and some non-printable characters as numbers, with each
letter assigned to a number from 0 to 127. 8-bit ASCII is used in C so the char datatype is one byte wide.

Fig 8 shows an ASCII chart that shows how different characters are represented in ASCII character-encoding scheme.
Character variables are stored in the memory as integers only, and therefore, we can perform computations using them.
For instance, in the example given below, %c format specifier tells printf() to interpret ch1 or ch2 as a character, and %d
tells printf() to interpret ch1 or ch2 as an integer while printing.

char ch1 = ‘A’, ch2; printf(“%c\t%d\n”, ch1, ch1); // prints A 65 on the screen ch2 = ch1 + 1; // char
variables can be used for computation just like integers printf(“%c\t%d\n”, ch2, ch2); // prints B 66 on the screen

Nonprintable characters

There are certain characters in ASCII like \n (newline), \t(tab), which cannot be printed on the screen. These are called
non-printable characters. Refer to Figure 8 to find out more non-printable characters.

To print some special characters like ‘ or “ or \ , we should use a preceding \. See the example below.

A string is a sequence of characters. In C, a string is implemented as an array of characters. (You will learn more about
arrays and strings later!) A character literal in C is enclosed within single quotes, whereas a string literal in C is enclosed
within double quotes. For example, “Welcome” is a string, ‘A’ is a character, “A” is a string containing only one character,
and ‘Welcome’ will throw an error.

Figure 8 - ASCII chart.

Type Conversions:

Type conversion in C allows us to tell the compiler to interpret the datatype of a variable differently in a particular
statement. Note that type conversion does not change the datatype of a variable; it just instructs the compiler
to interpret a variable as a different datatype while executing a particular instruction. There are two types of
type conversions, implicit and explicit.

• Implicit Type Conversion


o The compiler performs implicit type conversion when there’s an expression involving operands of
different datatypes, in which case the lower-sized operand would is elevated to the higher-sized
operand. For example, int a = 2; float b = 3.5, c; c = a + b; /* value of a in the expression is temporarily
promoted to float and two floats get added. The resultant float is again stored as integer in c.
o Rules for implicit type conversion are as follows:
§ If either operand is a long double, convert the other into a long double.
§ Otherwise, if either operand is double, convert the other into double.
§ Otherwise, if either operand is float, convert the other into float.
§ Otherwise, convert char and short to int.
§ Then, if either operand is long, convert the other to long.
o For example, float X = 0.2; double A = X/0.1; /* here X will be typecasted to a double variable and then the
division will be performed. A = 2.000000 after computation is over */
• Explicit Type Conversion
o In explicit type conversion, we explicitly instruct the compiler to interpret the datatype of a particular
variable differently while executing a particular instruction. Implicit type conversion is automatically
done by the compiler, whereas explicit type conversion must be done by the programmer explicitly.
o To do explicit type conversion, we can put the desired datatype of the variable in parentheses in front of
the variable. For example, int a = 3, b = 100; double c; c = b/a; printf("c = %lf\n",c); // c = 33.000000 c =
b/(double)a; /* here we perform explicit type conversion of a to double, and now the compiler will
convert the type of b to double implicitly */ printf("c = %lf\n",c); // c = 33.333333

Reading Summary:

In this reading, you have learned the following:

● What a variable is and what a constant is

● Declare and initialize variables and constants of different datatypes in C programs

● How variables in a program get space allocated in the RAM based on their types

● Find the size of different types of variables in C

● Differentiate between integer and floating-point variables

● Declare, print, and perform arithmetic operations on character variables

● Understand implicit type conversions in C and perform explicit type conversions in C


Reading: Operators and Expressions
Reading Objective:

In this reading, you will be introduced to various kinds of operators implemented in C and how to use them to create
expressions. You will also learn about the precedence and associativity rules to evaluate expressions.

Operators and Expressions in C

We have already got introduced to variables and constants. Now, we will define operators and operands and see how
these two are combined to form expressions. C programs employ different operators on one or more variables to get
useful results. Operators are mathematical symbols used in C programs to perform arithmetic, logical, and relational
manipulations on variables and constants. Operands are the values that operators operate upon to get some result.
When we combine operands with various operators, we get an expression. In C programs, operands can be constants,
variables, or even other expressions. For example, in the expression x + 2, the variable x and constant 2 are operands, and
+ is an addition operator. Other examples of expressions are 2 + 3, 2 *4, 8 - 3, 15 / 4, a + b / 7, -5, etc. Constants like 25
and -5 are also expressions.

Types of Operators

There are two types of operators: unary and binary operators. Unary operators operate on a single operand, whereas
binary operators operate on a pair of operands. For example, in the expression -5, the - sign is acting as a unary operator,
and in the expression 25 - x, the - sign is acting as a binary operator.

There is another way in which operators can be classified into four categories - arithmetic operators, relational operators,
logical operators, and assignment operators. Let us understand them one by one.

1. Arithmetic operators

• These operators are used for performing arithmetic operations on numeric types of data.
• Binary arithmetic operators take two operands as input. For example, C has the following binary arithmetic
operators:
o Addition (+): It computes the sum of two numbers.
o Subtraction (-): It computes the difference between two numbers.
o Multiplication (*): It computes the product of two numbers.
o Division (/): It computes the quotient upon the division of two numbers. For integer operands, the
division operator performs truncation to report the result. Truncation means that the result is reported
after dropping the decimal part of the number. For example, while rounding of 2.7 yields 3, truncation of
2.7 yields 2, rounding of -2.7 yields -3, whereas truncation of -2.7 yields -2.
o Modulo (%): It computes the remainder upon the division of two numbers. It is applicable only to integer
variables or constants. For example, 5%2 = 1. The modulo operator cannot be used on floating point
operands.
• The following example shows the result obtained by applying the given operands over two cases:
o Case 1: int a = 42, b = 5
o Case 2: float a = 42.0, b = 5.0

Integer Operands Floating Point Operands


Operator
(Case 1) (Case 2)
a+b 47 47.00
a-b 37 37.00
a*b 210 210.00
a/b 8 8.40
a%b 2 NA
2. Assignment operator

• This operator is used to assign a value to a variable. For example, y = 5; assigns the value of 5 to the variable y,
and x = y; assigns the value of y to the memory location corresponding to variable x.
• The variable occurring to the left of the assignment operator is called L-value. The L-value persists beyond that
single expression, i.e., the memory location of the L-value continues to hold the newly assigned value even after
that particular assignment statement until it is further modified.
• The temporary value that sits at the right of the assignment operator does not persist beyond the single
expression that uses it and is called R-value.
• There is another variety of assignment operators in C known as shorthand operators. These are of the form
“op=”. x op= y is the same as x = x op y; this is the reason why these operators are called shorthand operators
because they give a shorthand way to represent x = x op y; for example, +=, -=, *=, /=, %= are shorthand operators.
• Below given are two examples:
o The expression int x = 62, y = 7; x %= y; assigns the value (62 % 7), i.e., 6 to the variable x, whereas the
value of y stays the same.
o The expression, int dividend = 30, divisor = 4; dividend /= divisor; gives the dividend as 7 after the
operation, and the value of the divisor stays unchanged.

3. Unary arithmetic operators

• These operators take only one operator as input and can be used either as a prefix or as a postfix to the operand.
For example, ++ and -- are two unary arithmetic operators. ++ operator increments the value of the operand by 1,
and -- decrements the value of the operand by 1. These operators can only be applied to L-values.
• Prefix unary arithmetic operators:
o This type of unary operator changes the value and then uses the updated value in the expression. This
means that in the case of prefix unary arithmetic operators, the compiler first changes the value of the
operand and then uses that changed value in the expression.
o Syntax: ++ <variable_name> / -- <variable_name>
o For example, int a = 1; printf(“%d”, ++a); In the printf() statement, ++a first increases the value of a, and
then this new value is passed to the printf() function. Thus 2 gets printed.
o The prefix operator is lower in precedence than the postfix operator.
• Postfix unary arithmetic operators:
o This type of unary operator uses the existing value in the expression and then changes it. This means that
in the case of prefix unary arithmetic operators, the compiler first uses the current value of the operand
and then changes its value in the expression.
o Syntax: <variable_name>++ / <variable_name>
o For example, int a = 1; printf(“%d”, a--); In the above printf() statement, a-- first sends the current value
of a to printf(), and then decrements the value of a to 0. Thus number 1 gets printed. Subsequently, if you
try printing the value of a, it will print 0.
o The postfix operator is higher in precedence than the prefix operator.

4. Relational operators

• Relational operators are used to define relations between variables and constants. They compare numeric values
to determine if one is greater than, less than, equal to, or not equal to another. The result of the comparison is
either 1 (True) or 0 (False). The following are the different relational operators:
o a == b -> checks whether a is equal to b
o a != b -> checks whether a is not equal to b
o a < b -> checks whether a is less than b
o a > b -> checks whether a is greater than b
o a <= b -> checks whether a is less than or equal to b
o a >= b -> checks whether a is greater than or equal to b
• All of the relational operators have the same precedence and are left-to-right associative.
• We have to keep in mind that for a C compiler, the value 0 is considered False, whereas any non-zero value is
considered True, although relational operators result in value of 1 in case the expression is True.
• For example, consider the code given below. In the 7th relational expression, note that 2 >= -1 results in a value of
1, and eventually, -1 < 2 results in a False, which results in a value of 0.
5. Logical operators

• Logical operators are used for defining logical relationships between Boolean expressions in C. The word Boolean
means it can take only two values, either a 1(True) or a 0 (False). The following are the different logical operators:
o Logical AND denoted by && expr1 && expr2: This returns True when both the Boolean expressions
evaluate to True.
o Logical OR denoted by | expr1 || expr2: This returns True when at least one of the Boolean expressions
evaluates to True.
o Logical NOT denoted by ! !(expr): This returns True if expr is zero or False and returns False if expr is non-
zero or True.
• Logical operators always result in 1(True) or 0 (False).
• All logical operators have the same precedence and are left-to-right associative.

Operator Precedence and Associativity

When there are multiple operators with different precedence, then we use operator precedence to determine which
operation is to be performed first. On the other hand, when two operators of the same precedence appear in an
expression, then we use operator associativity. This associativity can be either Left to Right or Right to Left. Let us discuss
both of them in detail.

• In a C expression that contains more than one operator, the order of evaluation of the operators is decided by the
operator precedence rules in C. This helps the compiler to disambiguate the order of evaluation of operators. For
example, 2 + 3 * 6 can be interpreted as (2 + 3) * 6 = 5 * 6 = 30 or 2 + (3 * 6) = 2 + 18 = 20. The precedence rules in C
define that * should be evaluated prior to +, and thus the first interpretation is wrong, whereas the second one is
correct. Figure 1 shows the rules for operator precedence and associativity in C. The operators placed higher in
the table are given higher precedence as compared to operators placed on the lower side of the table. For
example, x = 2 + 3; + is higher than = in the table, and thus + is evaluated first, followed by =, and the result is that
x contains the value 5 after executing this statement.
Fig 1: Operator Precedence and Associativity rules in C (Source: lookuptables.com)

• In an expression, operators with the same precedence level are evaluated in the order specified by associativity
rules. For example, in the expression 2 * 3 / 6, * and / have the same precedence. Their associativity is from left to
right, i.e., the leftmost occurring operator in the given expression will be evaluated first, then the second left, and
so on, and the rightmost operator will be evaluated at last. In 2 * 3 / 6, * is evaluated first, which gives 6 / 6, and
then / is evaluated to give 1.
• Another example: 2 > 3 >= 2. Both > and >= have the same precedence. Thus, they are evaluated by the order of
their associativity (left to right). Firstly 2 > 3 evaluates to a 0 (False). Now the compiler will consider the value 0 for
the next relational operation. So, 0 >= 2 will compute to a 0 (False).
• C does not define the order in which the operands of an operator will be evaluated. This means that if there is an
expression of the form (exp1) op (exp2), then there is no pre-defined order of evaluation of the expressions exp1
and exp2, i.e., either of the two expressions can be evaluated first. For example, consider the code below:

Here, for the operator / whether the left operand (a+3) would be evaluated first or the right operand (c*2) would be
evaluated first is not defined.

Example Program to Convert a Given Temperature from Fahrenheit Scale to Celsius Scale

Let f represents the temperature in Fahrenheit scale, and c represents the temperature in Celsius scale. Both are related
to each other by the relationship given by c = (f - 32)/1.8

Now, if we consider giving input as Fahrenheit = 100, then after applying the operator precedence and associativity rules
to the expression, Celsius = (Fahrenheit – 32)/1.8, we will get the output of Celsius temperature as 37.777779.
Fig 2: C Program to convert temperature from Fahrenheit scale to Celsius scale

Reading Summary:

In this reading, you have learned the following:

• Operators and operands and how they are combined to form an expression
• The five types of operators in C, i.e., arithmetic operators, assignment operators, logical operators, relational
operators, and unary operators
• Precedence and associativity rules to evaluate expressions in C in an unambiguous manner
4 A Taste of C

WHAT TO LEARN

The three-phase compilation process that transforms a C source program to machine code.
Use of sequence, decision making and repetition in programs.
Handling simple errors generated by the compiler.
Role of functions in making programs modular.
Working knowledge of the printf and scanf functions.
Macro view of the features of the C language.
Commonly used styles that make programs readable and maintainable.
Standardization efforts that have made C stable and acceptable.

Sections—4.1-4.4, 4.5.1, and 4.6.

4.1 THE C LANGUAGE


The C language was created by Dennis Ritchie of AT&T (now Lucent) fame in the 1970s. C was
created with an unusual objective—not to replace Fortran or BASIC—but to rewrite the code for
the UNIX operating system. In those days, operating systems were written in assembly language,
but Ritchie’s out-of-box thinking produced a language that was portable, compact and fast.
In retrospect, we find that he achieved the best of both worlds. Today, the UNIX/Linux operating
systems run on practically any hardware, while C is widely favored for programming.
But the C originally created for UNIX systems is significantly different from the one used on
Windows computers (often, Visual Studio) or iPhones (Objective C). Ritchie’s C could make direct
calls (called system calls) to the UNIX operating system. The UNIX OS does all of its work using
a standardized set of these system calls. However, non-UNIX systems don’t use most of these calls,
so UNIX C programs containing these calls would fail to compile on other systems. But C had too
many advantages to be ignored for this reason alone.
When C went commercial, vendors made numerous changes to the language to let it run on
other platforms. The original C gradually lost its identity until the American National Standards
122 Computer Fundamentals & C Programming

Institute (ANSI) stepped in to standardize the language. The ANSI standard removed the system
calls from UNIX C and retained a subset of the original language. This subset is now common to
all systems, and in this book, we’ll stick to this subset, which will be variously referred to as ANSI C,
C89 or C99, the nomenclature used by ANSI.
C is a simple but powerful third-generation language. Its rich library of functions enables the
development of modular applications. Its support of a variety of constructs for decision making
(if and switch) and repetition (while, for and do-while) also makes it suitable for structured
programming. But the language derives its power from the vast arsenal of operators that permit
low-level operations like (i) direct access to memory, and (ii) manipulation of every bit in memory.
In this respect, C is somewhat akin to a second-generation language like assembly language.
Today, C is widely adopted for developing a diverse range of both application and system software.
Compilers for many languages (including C) are written in C. Since C programs are compact and
efficient, it is the preferred language for software running on small devices like mobile phones and
tablets. C has replaced BASIC and Pascal for introducing students to the programming world.
It has also influenced the development of object-oriented languages like C++, C# and Java.
It pays to know C because the language is not going to go away in a hurry.
Note: C can be considered to be both a high- and low-level language. While it is sufficiently low-level
to enable access to specific memory locations, it is also a high-level language with all the standard
constructs and operators found in any 3GL language.

4.2 THE LIFE CYCLE OF A C PROGRAM


In this section, we discuss the role of the agencies involved in making a C program run successfully.
A program is conceived, created, transformed and eventually executed (Fig. 4.1). Errors invariably
occur during (i) compilation on account of faulty use of syntax (grammar), (ii) execution because of
erroneous program design. Before we discuss the compilation mechanism, it is necessary to discuss
two important features of C that significantly impact the compilation process.

4.2.1 Header Files and Functions


Ritchie wanted C to be compact, so two features—directives and functions—were kept “outside”
the language. For instance, most C programs have the line #include <stdio.h> at the beginning of
a program. This is simply a directive to place the contents of the header file, stdio.h, in the program.
A header file contains data used by multiple programs, so rather than include the data in the program
itself, a one-line directive keeps things simple and manageable. Also, by judicious division of labor,
C ensures that this line is processed by a preprocessor so that the compiler doesn’t see it at all.
In C, most work is performed by functions. A function is called by its name to execute a set of
statements. If multiple programs need to perform this task, then they can call the same function.
This is possible only if the machine code for the function is available centrally and made accessible
to all. For instance, when the compiler encounters the printf function, it knows that the code for
printf is available elsewhere. Division of labor works here as well; the compiler takes a hands-off
approach and leaves the job of including the function code to the linker.
A Taste of C 123

Text editor or IDE

Source code
foo.c (text file)

Preprocessor

Preprocessed code
foo.1 (text file)

Compiler

Assembly code
foo.s (text file)

Assembler

Object code
foo.o or foo.obj
(machine language)
Libraries

Linker

Executable code
Other object a.out or aout.exe or
files foo.exe
(machine language)

Loader

Program image
in memory

FIGURE 4.1 The Life Cycle of a C Program


124 Computer Fundamentals & C Programming

4.2.2 Editing the Program


Before writing a program, you need to first identify its input and output and then develop the
program logic. Once you have the algorithm or flowchart ready, key in the source code using an
editing software. This editor may be provided by the operating system or it may be a component of
the compiling software. Save the code in a file having the .c extension using a meaningful name
(like celsius2fahrenheit.c instead of names like a.c or foo.c).
For a program to compile successfully, its source code must be syntactically correct, i.e. it must
conform to the rules of the language. Make a small mistake (like dropping the ; in a C statement)
and your program will refuse to compile. Every time one or more errors are generated, you have to
re-invoke the editor to carry out the changes. The file containing the source code must be backed
up to allow for future modifications. If you lose this file, you cannot reverse-engineer the machine
code to retrieve the source code.

4.2.3 The Three-Phase Compilation Process


As just noted, a C program depends on information maintained externally, so the compilation
procedure in C is somewhat different from other languages. Compilation in C is a three-step
process that involves the use of three separate programs:
The Preprocessor This program acts on lines that begin with a #. They occur mostly at
the beginning of a program and are called preprocessor directives. A directive may insert the
contents of a header file at that location (like #include <stdio.h>) or perform a substitution
(like #define PI 3.142) in the program itself. The preprocessor thus modifies the source code
to make it suitable for use in the next phase. This modification is carried out on a copy;
the original file containing the source code is left undisturbed.
The Compiler This program checks the source code after it has been modified by the
preprocessor. If the compiler detects one or more syntax errors in this code, it displays them
and stops further processing. Otherwise, the compiler creates intermediate object code.
This code is incomplete and not ready to run because it doesn’t contain the code for the
standard C functions (like printf and scanf). The compiler merely leaves unresolved references
to these functions (3.15).
The Linker This program acts on the unresolved references left behind by the compiler.
The linker does this by adding the machine code of functions available in separate libraries to
the object code created by the compiler. The resultant code is a standalone executable that is
run by the program loader. The linker fills the holes left by the compiler.
Even though every C distribution contains the three programs, they may or may not be invoked
separately (unless you are handling an application that comprises multiple programs). Generally,
these programs are invoked by one principal program, but it will stop in its tracks if it encounters
syntax errors in the source code. For instance, the linker won’t be invoked if the compiler detects
syntax errors, which means that the executable won’t be created. If that happens, you have to edit
the source file and repeat this three-phase cycle.
A Taste of C 125

Does the compiler directly translate the source code to machine code? Some compilers (like the
Linux GCC compiler) find it more convenient to perform translation to assembly language first.
A separate program (the assembler) then converts the assembly language code to machine code.
The intermediate file is removed once the final machine code is created. However, GCC also
supports an option that retains the assembly code for use by assembly language programmers.
Note: The term compiler is used in two ways in C. It could mean the program that does the job
specified in the second phase, or represent a collective term that performs all of the three tasks.
The true meaning should be evident from the context, but when it is not, we’ll use the term compiling
software to refer to the collective meaning.

4.2.4 Executing the Program


The default name of the standalone executable is system-dependent—a.out on UNIX/Linux
systems, and aout.exe or AOUT.EXE on Windows. (Visual Studio names the executable after the source
file, so foo.c is compiled to foo.exe.) This file can be executed by running ./a.out or .\aout from
the operating system’s shell. If you are using C in a GUI environment (like Visual Studio), you have
to use the mouse to select menu options that perform all of the compilation and execution tasks.
It is possible for a program to pass the compilation and linking phases and still fail in execution.
Programs fail at runtime (i.e., when the program is actually run) on account of semantic errors that
arise out of mistakes in design. For instance, a division by zero makes the program abort with an
error. Use of = instead of == will also yield unexpected results. Faulty logic that doesn’t account for
all possibilities can make a program behave erratically as well. The solution is the same; edit the
source code and repeat the compilation process before re-execution.
You might argue that if the contents of stdio.h are made available by a simple #include directive,
why not a value (like 3.142 or PI) that is required by multiple programs involving circles? That’s
right, C doesn’t encourage you to store the value of PI in every program that computes the area of
a circle. You can keep this information in a header file and include it as well.

Takeaway: A program can have syntax errors (on account of wrong use of its grammar) or
semantic errors (because of faulty program logic). The former is detected during compilation
and the latter during program execution.

4.3 KNOW YOUR C COMPILING SOFTWARE


Before you start writing programs, you need to be familiar with the C compiling software
(the entire set of programs) installed on your system. Over the years, the footprint of this software
has grown in both size and convenience to provide a complete programming environment.
This environment generally comprises the following components:
The main programs—the preprocessor, compiler and linker.
The standard header files interpreted by the preprocessor.
126 Computer Fundamentals & C Programming

The standard library containing the object code of all functions that are part of ANSI C
(like printf).
An editor to edit programs and header files.
A debugger to locate semantic (runtime) errors in programs.
Compiling software is available in both CUI and GUI flavors. Command-line or CUI systems offer
separate programs for editing, processing and debugging. These programs are invoked by name
and they support several options or switches that change their default behavior. We won’t need to
use these options in this book though (except in Chapter 17).
GUI-based systems offer an Integrated Development Environment (IDE) where all actions are
invoked by the point-and-click mechanism of the mouse. IDEs are menu-based which dispenses
with the need to invoke commands by name. Behind the hood, however, these mouse clicks
internally invoke standalone programs. Generally, IDE systems also offer a CUI mode for those
(this author included) who prefer to invoke commands by name.

4.3.1 GCC (Linux)


Linux systems support a CUI system called GCC, the free-to-use package from GNU. It is shipped
with every Linux distribution but may not be installed by default. It comprises the following
standalone programs: gcc (compiler), cpp (preprocessor), ld (linker) and gdb (debugger). GCC
doesn’t offer an editor, but Linux supports two powerful editors that are used by most people: vim
and emacs. To check whether GCC is installed on your system, run the gcc command from a shell:
$ gcc The $ signifies the shell prompt
gcc: no input files gcc exists
This message from the gcc compiler points out that a filename needs to be specified. If the GCC
package is not installed, you’ll get the message gcc: command not found. gcc also acts as a frontend
to cpp and ld, so you don’t normally have to bother about running the preprocessor and linker
separately. By default gcc creates the executable a.out which has to be executed as ./a.out.

4.3.2 Microsoft Visual Studio (Windows)


Unlike in Linux, C compiling software is not native to the Windows operating system. But
numerous third-party packages are available including the GCC package that has also been
ported to this platform. For this book, we’ll refer to the popular GUI-based Visual Studio package,
where the Build menu in the menu bar offers all the necessary processing options. The essential
programming activities and their associated menu options are listed below:
Compile the program with Build > Compile.
Invoke the linker with Build > Build.
Execute the program with Build > ! Execute.
Unlike GCC, Visual Studio supports a built-in editor having limited capabilities. It also permits
command-line operation from an MSDOS shell. The commands to compile and link are cl.exe
and link.exe, respectively. Like gcc, cl.exe directly invokes the linker (link.exe) to create the
A Taste of C 127

executable. Unlike other Windows compilers that generate aout.exe as the default executable,
Visual Studio names the executable after the source filename. The source file first_prog.c is
compiled to first_prog.exe.
Note: Windows executables have the .exe extension. Depending on the compiler used, the program
foo.c is converted to the executable aout.exe or foo.exe. Also note that Windows filenames are
not case-sensitive, so foo.c and FOO.C, which are treated as separate files by Linux, will create conflicts
when moved to Windows.

4.4 first_prog.c: UNDERSTANDING OUR FIRST C PROGRAM


Our first program (Program 4.1—first_prog.c) sequentially executes a set of statements that
perform a simple arithmetic operation using two variables. The output is displayed on the screen.
This program is not designed to pause for taking user input from the keyboard; all data are
provided in the program itself. A cursory glance reveals a typical two-part structure comprising the
preprocessor and body sections. The spirit of C is evident in the annotations and program output.
/* first_prog.c: Declares and assigns variables.
Also features some simple computation. */

/* Preprocessor Section */
#include <stdio.h> /* printf needs this file */
#define PI 3.142 /* PI is a constant */

/* Program Body */
int main(void) /* This is where the action begins */
{
/* Variable declarations */
int radius = 5; /* Integer type */
float area; /* Floating point type */

/* Other Statements */
printf(“All numbers are stored in binary\n”);
printf(“Radius of circle = %d\n”, radius);
area = PI * radius * radius;
printf(“Area of circle having radius %d = %f\n”, radius, area);

/* The return statement explicitly terminates a program */


return 0;
}
PROGRAM 4.1: first_prog.c
All numbers are stored in binary
Radius of circle = 5
Area of circle having radius 5 = 78.550003

PROGRAM OUTPUT: first_prog.c


128 Computer Fundamentals & C Programming

4.4.1 Program Comments


Like every language, C supports comments in the source code. Comments help in making
program maintenance easier, so a sensible programmer provides them at key points in a program.
They are enclosed by the character sequence /* on the left and */ on the right. Comments can be
single- or multi-line and they can also be placed after program statements. This program shows
all of these instances.
Comments in no way affect the size or efficiency of the executable because they are removed by
the compiler. While excessive use of comments is discouraged, complex logic must be properly
documented in the program itself. Judicious use of comments will help you understand your own
program when you look at it months or even years later.

4.4.2 Preprocessor Section


The preprocessor section that follows the initial comment lines contains two lines that begin
with a # but don’t end with a semicolon. They are not C statements because all C statements are
terminated with a semicolon. (For confirmation, look at the lines that follow.) These lines, called
preprocessor directives, generally appear before the occurrence of the word main. When the program
is compiled, the preprocessor acts on these lines before the compiler sees them.
The first directive (#include <stdio.h>) tells the preprocessor to replace the line with the contents
of the file stdio.h. This header file, which contains information required by the printf function,
is shipped with every C distribution.
The next directive (#define PI 3.142) instructs the preprocessor to globally replace all occurrences
of PI in the program with 3.142. The compiler thus doesn’t see PI but only 3.142. Unlike radius
and area which are variables, PI is a symbolic constant. It occurs once in the body—in the expression
that calculates the area of a circle. We will have to understand why PI was not declared as a variable.
Note: Preprocessor directives don’t end with a semicolon because technically they are not
C statements. In fact, the compiler, which acts after the preprocessor has completed its job, doesn’t
see any comment lines, #define or #include directives.

4.4.3 Variables and Computation


The preprocessor section is followed by the program body, which begins with the word main. The
body represents the meat of a C program where all work is actually done—assigning variables,
making decisions, running loops and invoking function calls. The body contains statements that
end with a semicolon. Here, it begins with two variable declarations:
int radius = 5; /* Integer type */
float area; /* Floating point type */
C is a typed language, which means all data have types assigned to them. The first statement
declares a variable named radius having the type int (integer). The assignment symbol, =, is an
operator which assigns the value 5 to radius. The next statement declares another variable named
A Taste of C 129

area of type float (floating point). These C keywords, int and float, are two of the frequently used
data types in C. At this stage, we have no idea what the initial value of area is.
The next statement (area = PI * radius * radius;) uses the multiplication operator, *, to calculate
the area of a circle. The result is then assigned to the variable area. Note that the compiler doesn’t
see PI, but 3.142, since the preprocessor has already made this substitution. We have now seen the
use of two members (= and *) of C’s vast arsenal of operators.
Note: All variable declarations should be made at the beginning—after the preprocessor directives
and before the statements.

4.4.4 Encounter with a Function: printf


C uses printf to output messages on the screen. Unlike int and float, which are C keywords,
printf is a function. A C function is easily identified by the pair of parentheses that follow its name.
The first printf uses a single argument—the string “All numbers are stored in binary\n”. Note that
printf doesn’t print \n which it interprets as an escape sequence. In this case, \n moves the cursor
to the next line, so the next printf can display output on a separate line.
The next two printf statements use two or three arguments delimited by commas. In each case,
the first argument must be the string to be printed. But if this string contains a format specifier
(like %d), then the next argument is printed at that location. %d is used to print integer values while
%f handles floating point numbers. In these two statements, %d is the format specifier for radius,
while %f is the specifier for area.
Note: printf will not insert a newline unless you explicitly direct it to do so. This feature enables
printing of long strings with separate printf statements.

The main Function Is printf the only C function we find in this program? No, we should have
known that the word main is actually a function; it too is followed by a pair of parentheses. For now,
let’s simply note that every program has a main function which is automatically invoked to execute
the statements in the program body. We will discuss this function in Section 11.15.

4.4.5 The return Statement


Finally, we meet the return statement which is usually the last statement in the program body.
Program execution terminates when this statement is encountered inside the program body. After
we have acquired knowledge of functions, we’ll use return inside functions also. For now, we’ll
ignore the significance of the value 0 but not fail to use return with this value.
With the limited knowledge acquired so far, we have somewhat understood how first_prog.c
works. But for a genuine first-hand feel, we also need to key in the program and run it to obtain
the output shown below the program listing. We’ll do this after we have examined the software
environment that we use to compile and run our programs.
130 Computer Fundamentals & C Programming

4.5 EDITING, COMPILING AND EXECUTING first_prog.c


Make a separate directory on your system for the C programs that you'll develop. Using a text editor,
key in this program to create the source file, first_prog.c. We use a text editor instead of a word
processing software like Microsoft Word because C source programs are text, and not binary, files.
Using the following guidelines would stand you in good stead:
Indent the text as shown in the program listing. Proper indentation helps in both
understanding and maintenance, so use the [Tab] ( )key to provide indentation.
Don’t fail to provide the ; which is seen at the end of most lines.
Don’t terminate statements beginning with #define and #include with a semicolon.
C is case-sensitive, so don’t use uppercase except where shown.
Don’t use single quotes where double quotes have been specified, and vice versa.
After you have input your program, save it as first_prog.c. You are now ready to start the compilation
process. Compiler-specific information follows next, so you will need to refer to either Section 4.3.1
or Section 4.3.2.
Caution: Remember to terminate every C statement with the semicolon. A compiler generates
errors if it doesn’t find it even though it may not properly identify the location where the semicolon
should have been placed.

4.5.1 Using gcc (Linux)


To input your program, you may choose the vim or emacs editors, two of the most notable and
powerful editors supported by Linux. (Versions for Windows are also freely available.) After saving
the file, use the gcc command to compile first_prog.c:
$ gcc first_prog.c
$ _ Prompt returns; compilation successful
By default (as above), gcc first invokes the preprocessor (cpp), then does the job of compiling itself,
and finally calls the linker (ld) to create the executable—all in a single invocation. The silent return
of the prompt indicates that no syntax errors have been encountered and that the default executable
file, a.out, has been created. Run the ls command now to verify the presence of this file.
You can now invoke the executable in this manner to produce the output shown after the program
listing (Program 4.1):
$ ./a.out
All numbers are stored in binary
Radius of circle = 5
Area of circle having radius 5 = 78.550003
The output is correct, which means there are no semantic errors in your program. In case you see
syntactical error messages, note the line numbers of the offending locations from these messages.
Correct the source code and recompile the program. Handling error messages is taken up in
Section 4.6.
A Taste of C 131

4.5.2 Using Visual Studio (Windows)


As mentioned previously, Visual Studio supports both GUI and CUI modes of operation. Key in
your program using the built-in editor of Visual Studio or Windows Notepad, and save the file as
first_prog.c. The sequence of steps for each of these modes is explained next.

GUI Mode The Build menu in the Menu bar has all the necessary options for compilation and
execution. In this mode, you need to invoke the compile and link phases separately:
Preprocess and Compile Use Build > Compile first_prog.c and observe the window pane at the
bottom. If you see compilation errors, note their line numbers, edit the source file and repeat
the compilation. You may ignore the warnings for the time being. On successful compilation,
you should see the message first_prog.obj - 0 error(s), signifying that the object file,
first_proj.obj, has been created. This file contains machine code but is not ready to run
because it contains unresolved references (3.15).
Link Linking is done with Build > Build first_prog.exe, when you should see the message
first_prog.exe - 0 error(s). The executable, first_prog.exe has been created.

FIGURE 4.2 Visual Studio: Compiling and Executing a Program


132 Computer Fundamentals & C Programming

You can now run this executable with Build > ! Execute first_prog.exe. A new window will open
up with the output of the program. The screen dumps of these activities are shown in Figure 4.2.

Tip: Visual Studio offers shortcuts for all three activities: [Ctrl][F7] for compiling, [F7] for linking
and [Ctrl][F5] for executing. Also, you’ll find separate icons representing these activities in one of
the toolbars.

CUI Mode Invoke the MSDOS shell or command prompt (using Run > cmd) and then invoke
the following command:
C:\> cl first_prog.c
/out:first_prog.exe
first_prog.obj
The program cl.exe invokes link.exe to create the executable, which can then simply be run like
this:
C:\> .\first_prog Note the .\
All numbers are stored in binary
Radius of circle = 5
Area of circle having radius 5 = 78.550003
The output is correct, which means there are no semantic errors in the program. In case you
encounter errors, locate the line numbers provided in the error messages to edit the source code.
Recompile the program and re-execute first_prog.exe. Handling error messages is taken up in
Section 4.6.
Note: The standalone executable (a.out or first_prog.exe) can now run on this and similar machines
even if the C compiler is not installed on these machines.

4.6 HANDLING ERRORS AND WARNINGS


What you see is often not what you expected to see—at least not in the initial phase of the learning
process. Errors are inevitable but you must able to detect and correct them. Programmers often
forget to add the semicolon, and if you do that in the first printf statement of first_prog.c, this is
how gcc reacts on Linux:
$ gcc first_prog.c
first_prog.c: In function main:
first_prog.c:18: error: expected ; before printf
There seems to be no ambiguity in the message; the compiler spots a missing semicolon on line
18. But, unexpectedly perhaps, line 18 contains a semicolon:
printf(“Radius of circle = %d\n”, radius);

Did the compiler get the line number wrong? Not really; it’s just that the missing ; on line 17 lured
the compiler into assuming that lines 17 and 18 constituted a single statement. Since the statement
terminator is a semicolon, the compiler keeps scanning until it finds one.
A Taste of C 133

Visual Studio also displays a similar message, but it generates a warning as well:
first_prog.c(16) : warning C4244: ‘=’ : conversion from ‘double ‘ to ‘float ‘,
possible loss of data
first_prog.c(18) : error C2146: syntax error : missing ‘;’
before identifier ‘printf’
Error executing cl.exe.
first_prog.obj - 1 error(s), 1 warning(s)
This warning will make sense after you have learned that, like variables, constants have data types
(5.1.2). Here, the constant PI has the type double, which on this machine is longer than float.
The compiler is worried at the possible loss of information that could be caused by assigning
a value of a larger type (double) to a variable (area) of smaller type (float). A warning is not an
error and the code will still compile. However, you should know what you are doing, so after some
time, you should be able to handle these warnings as well.
The omission of the ; at the end of a C statement is an example of a syntax error. A syntax error
occurs when a statement fails to conform to the rules of the C language. The compiler doesn’t
generate the executable when such an error occurs. The previous error message helped us locate
the offending line without any problem, but some errors could be a little difficult to locate.
However, even if you manage to successfully clear the compilation and linking phases, your
program may still not behave in the manner intended. You may have entered = instead of == (both
are valid C operators), or 65 instead of 65.0. These are semantic errors which signify errors in
meaning. For instance, if (x == 5) is not the same as if (x = 5), even though both are syntactically
correct. Semantic errors are encountered only at runtime, i.e., when the program is actually run.
The compiler doesn’t detect them; it’s your job to do that—using a debugging program if necessary.
Tip: Syntax errors are normally located early, but semantic errors can often be difficult to identify.
These errors may not show up in every invocation of the program simply because some data may
cause problems but some may not. Pay attention to the warnings; they often provide valuable clues.

4.7 second_prog.c: AN INTERACTIVE AND DECISION-MAKING PROGRAM


You are aware that structured programming requires a language to support (i) decision making
and (ii) repetition. Our next program (Program 4.2) uses the if statement for making decisions.
It also uses the scanf function to implement interactive behavior. The program takes an integer from
the user, validates the input and prints the ASCII character having that value. Four invocations of
the program reveal important information that you would like to keep in mind at all times.
Note the #include statement which should always be included whenever we use functions like
printf and scanf. When the program encounters scanf, it pauses to take user input. scanf then
converts and saves this input value in the variable your_number.
What is striking here is the use of the & prefix for the variable your_number when used as an
argument to scanf. By design, C functions can’t directly assign keyboard input to variables. Section
4.8.2 explains the necessity of conversion and why scanf needs the & even though printf doesn’t.
Data—Variables and
5 Constants
Sections 5.1-5.5, 5.6.1 - 5.6.3, 5.7, and 5.11-5.14.

WHAT TO LEARN

Concept of data held in variables and constants.


Classification of data into fundamental and derived types.
Attributes of the integer and floating point data types.
Why the char data type is special.
How attributes of constants differ from variables.
Overview of arrays and strings as derived data types.

5.1 PROGRAM DATA


We deal with data in everyday life. Data often represent the attributes of real-life objects—like
name, weight, quantity or price. Because computer programs are often closely linked with real-life
situations, they need data too. In fact, it is impossible to devise a meaningful program that doesn’t
need data. Even though instructions make a program do all the work, it’s the data that make the
same program behave in different ways.
A program must know the address in memory of every piece of data that it handles. When
a program encounters the variable x, it can evaluate this variable only if it knows the memory
location of x. As a programmer, you need not know the address of this location or the register where
it is moved to because the compiler provides this information in the executable. Keep in mind that
you are learning to use C and not assembly language.

5.1.1 Variables and Constants


Program data occur in the form of variables and constants. A variable has a name that is assigned
a value. This value is stored in memory and can change while the program is running. We used
the variables area, your_number and counter in the programs of Chapter 4. The name used to
access a variable is also known as an identifier. (Arrays and functions also have names, hence they
too are identifiers.)
Data—Variables and Constants 149

Constants represent data that don’t change. In the expression (fahrenheit - 32) / 9.0, both
32 and 9.0 are constants. The first argument used with printf is often a string constant, say,
“Printing ASCII list”. Like variables, constants also occupy space in memory. These constants
have no names, but later in the chapter we’ll encounter constants that have names. We’ll also have
a look at variables that behave like constants.

5.1.2 Data Types


Because both numeric and string data are stored as binary numbers, how does a program
distinguish between them? C is a typed language, which means that every data item—variable or
constant—has a type associated with it. To consider variables first, its type is clearly visible in the
declaration:
int counter;
The compiler identifies the type as int, i.e. integer, and allocates at least 2 bytes of memory for the
variable named counter. int is one of several data types in C. The highlight on “at least” signifies
the inability of C to specify exact sizes for variables and constants (and we’ll soon know why).
All constants (like 100, 3.142, ‘A’, etc.) also have data types and we must know them.

5.1.3 Data Sources


A program obtains its data from different sources, and the C language has different constructs for
handling these sources. We have already dealt with the first two of the following three sources in
Chapter 4:
Embedded in the program (as variables and constants).
User input captured by functions like scanf (a feature of interactive programs).
External files that are read by file-related functions supported by C.
There is at least one more source of data (command-line arguments), but we’ll postpone discussions
on it until we have understood arrays and strings. Files are also taken up later, so we will stick to
the first two data sources for the time being.

5.2 VARIABLES
A variable is a name given to a memory location that is accessed by that name to fetch or set
a value at that location. Variable usage is either mandatory or compelling. For instance, if a while
loop has to execute a finite number of times, you need a variable to determine when the loop will
terminate (mandatory). On the other hand, if the rupee-dollar conversion rate is used multiple
times in a program, then this value is better stored in a variable (compelling). Here, the variable
behaves like a constant while the program is running.
5.2.1 Naming Variables
The rules for framing variable names apply to all identifiers, which include arrays and functions.
A variable name can begin with a letter or the underscore (_) character, but the remaining part of
the name can comprise only the following characters:
150 Computer Fundamentals & C Programming

Letters (both lower- and uppercase)


Digits
The underscore (_) character. Note that this is different from the hyphen (-) which is available
on the same key as the _ on your PC or laptop.
A variable name thus cannot begin with a digit. The names rate1 and _rate1 are valid but not
1rate. Variable names are also case-sensitive; rate1 and RATE1 are two separate variables. Here are
some examples of valid identifier names:
x ABC name024 sampling_speed _month __file
Under C89 (the first C standard from ANSI), the first 31 characters of an identifier are significant.
The remaining characters are ignored by the compiler. The following keywords used in C cannot
be used as variable names:
auto continue enum if short struct unsigned
break default extern int signed switch void
case do float long sizeof typedef volatile
char double for register static union while
const else goto return
The C language also uses a number of variables but they are in uppercase. To distinguish
user-defined variables from the ones used by C, you should declare variables only in lowercase.

Tip: Use meaningful lowercase names for variables. Names like x and abc are valid, but they don’t
intuitively reveal what they represent—unlike variables like sampling_speed and conversion_rate.

5.2.2 Declaring Variables


A variable must be declared before use. In most cases, the declaration is made at the beginning of
the program (mostly after main, but sometimes before main for global variables). The following
statement declares a variable named bits_per_sec:
int bits_per_sec; ; shows declaration is a statement
Here, the variable bits_per_sec has the type int, one of the integer types supported by C.
On seeing this type, the compiler allocates at least two bytes in memory for bits_per_sec. It also
notes the starting address of that memory location so it knows where to look up when encountering
expressions that use the variable. We have still not explained the significance of the highlight on
the words "at least."
Using the comma as delimiter, multiple declarations can be made in a single statement provided
the type is the same:
int bits_per_sec, sampling_speed; Variables must have same type
On modern machines, an int variable uses 4 bytes of memory, which is also the size of the word.
However, when the CPU extracts these 32 bits from memory it must also know that these bits have
Data—Variables and Constants 151

to be interpreted as a single binary number. This knowledge is vital because a chunk of 4 bytes can
be interpreted as an integer, a floating point number or a three-character (not four) string.

5.2.3 Assigning Variables


The previous declarations did not assign initial values to the variables. To initialize a variable,
use the = operator. One way is to separate the declaration from the assignment:
int bits_per_sec; First declare
bits_per_sec = 0; Then assign
Alternatively, you can combine both activities:
int bits_per_sec = 0; Both in one statement
For declaring and initializing multiple variables of the same type, use the comma as delimiter.
The following statements declare and initialize all variables to the same value:
float amount1, amount2, amount3;
amount1 = amount2 = amount3 = 1000.00;
However, to assign separate values, use the comma in the initialization as well:
amount1 = 500.50, amount2 = 600.75, amount3 = 200.25;

You can even combine the declaration and initialization:


float amount1 = 500.50, amount2 = 600.75, amount3 = 200.25;

The value of an uninitialized integer variable is indeterminate (i.e., junk); it can’t be assumed to
be zero. The value is actually one that was resident in memory when the program was executed.
Never use an uninitialized variable in an expression!

Tip: Even though a variable declaration can occur anywhere in a program as long as it precedes
usage, it is good programming practice to bunch all declarations immediately after main. However,
global variables (differerent from the ones discussed here) have to be declared before main (14.8).

5.3 intro2variables.c: DECLARING, ASSIGNING AND PRINTING VARIABLES


Program 5.1 declares four variables, assigns them values and then prints them. Two variables are
of type int and two have the type float. The program also shows how a variable is assigned from
keyboard input.
Out of the four variables, only one is uninitialized (float_x1). No wonder this variable displays junk
the first time it is printed (-0.000029). float_x1 is later assigned with (i) =, (ii) scanf. This function
halts execution to let you key in a floating point (real) number. In both cases, the value assigned or
keyed in is not exactly the one you see in the printf output. Floating point numbers can’t always
be exactly represented in memory.
152 Computer Fundamentals & C Programming

/* intro2variables.c: Declares, assigns and prints variables.


Also assigns a variable by keyboard input. */
#include <stdio.h>
int main(void)
{
/* Variables must be declared before they are used */
int int_x1, int_x2, int_x3 = 64; /* Partial initialization */
int_x1 = int_x2 = -22; /* Simultaneous initialization */
float float_x1; /* Uninitialized variable */
float float_x2 = 30.48;

printf(“int_x1 = %d, int_x2 = %d, int_x3 = %d\n”,


int_x1, int_x2, int_x3);
printf(“float_x1 = %f, float_x2 = %f\n”, float_x1, float_x2);

/* Once declared, a variable can be assigned anywhere .... */


float_x1 = 1203.67;
printf(“float_x1 = %f\n”, float_x1);

/* ... and also reassigned, this time with the scanf function */
printf(“Now key in any real number: “);
scanf(“%f”, &float_x1);
printf(“float_x1 = %f\n”, float_x1);
return 0;
}

PROGRAM 5.1: intro2variables.c


int_x1 = -22, int_x2 = -22, int_x3 = 64
float_x1 = -0.000030, float_x2 = 30.480000
float_x1 = 1203.670044
Now key in any real number: 1234.56789
float_x1 = 1234.567871

PROGRAM OUTPUT: intro2variables.c


Caution: If you don’t initialize a numeric variable in your program, it will have a junk value to begin
with. Never assume this value to be zero. Not all variable types have junk initial values though.

5.4 DATA CLASSIFICATION


C is a small language but it is generous in its offering of data types from where you should be able
to select the right type and subtype. These types can be broadly categorized as follows:
Fundamental (like int, float, etc.)
Derived (like arrays and pointers)
User-defined (structures, the typedef and enumerated types)
Data—Variables and Constants 153

The derived types are variations of the fundamental types. C also allows you to define your own data
types for handling complex data. However, the language doesn’t have a separate type for strings.
In this chapter, we’ll discuss the fundamental types and briefly understand how C implements
a string.

5.4.1 The Fundamental Types


In our previous programs, we have used only the fundamental data types—explicitly for variables
and implicitly for constants. (A constant like 32 or ‘A’ also has a fundamental data type.)
The fundamental types can be further divided into the following three subtypes:
Integer (the keywords short, int, long and long long)
Floating point (the keywords float, double and long double)
Character (the keyword char)
The declaration of any variable of the fundamental types must begin with one of these keywords.
The subtype determines two things: the amount of memory allocated for the variable or constant
and the scheme used for storage. Unlike integer data, a real number like 32.5 is not stored as a single
contiguous number but as a combination of two integers (5.8).
Constants also have types, but unlike variables, their type information is conveyed implicitly to the
compiler. When the compiler looks at constants like 3, 3.0 or “Jelly Bean”, it interprets 3 as an
int, 3.0 as a double, and “Jelly Bean” as an array of type char. The upcoming discussions on the
three subtypes apply fully to variables and partially to constants.

5.4.2 Type Sizes: The sizeof Operator


The compiler knows the size of every subtype even though K&R C (the first definition of the
language) deliberately refrained from specifying fixed sizes for them. To enable C to run on all
types of hardware, K&R C prescribed only minimum sizes along with a relationship between them.
This explains the use of the words “at least” in Sections 5.1.2 and 5.2.2.

HOW IT WORKS: Declaration vs Definition


We have declared variables before using them, but it can also be said that we have defined them.
A declaration of an object makes its type information available to the compiler but it doesn’t
create memory for the object. The compiler simply knows how to store and interpret the object
in memory. It’s the definition that actually allocates space in memory for the object.
We have not bothered so far to make this distinction because the statement int x; not only
makes type information of x available to the compiler (declaration) but also creates space in
memory for x (definition). When you declare a variable, you also define it.
However, declaration and definition represent separate activities for a function or a structure.
You have seen in Section 4.11 how a function is first declared before main and defined after main.
154 Computer Fundamentals & C Programming

ANSI’s endorsement of the K&R C prescription (the first C definition from Kernighan and
Ritchie) of minimum sizes needs to be kept in mind when developing portable applications.
Even though ANSI specifies the minimum size of int as 2 bytes, your machine will in all probability
have a 4-byte int. And if portability is not a concern, there is no reason why you shouldn’t take
advantage of this enlarged size.
The sizeof operator evaluates the size of all data types—the fundamental types, pointers, arrays
and constants. This is how you can find the size of the int data type on your computer:
printf(“Size of int: %d\n”, sizeof(int));
This should print 4 on your PC. Note that sizeof is not a function even though it looks like one.
It is an operator (like = and +) which works on an operand, which here is enclosed within parentheses
in function-like manner. The next program uses sizeof both with and without parentheses.

Takeaway: There are no fixed sizes for the fundamental data types, but only ANSI-specified
minimum sizes. This was done to protect the applications that were developed prior to the
ANSI standard.

5.5 sizeof.c: DETERMINING THE SIZE OF THE FUNDAMENTAL DATA TYPES


Program 5.2 uses sizeof to determine the size of all the fundamental data types on your computer.
It operates on two constants as well. The last printf statement which checks for the long long
type has been commented. To know whether your compiler supports this data type inducted by
C99 (the next ANSI standard after C89), uncomment this line. The output is system-dependent,
the reason why it is shown separately for Linux (GCC) and Windows (Visual Studio).
The size of char is always 1 byte because, by definition, it was designed to hold the entire character
set of the machine. Observe that both int and long are 4 bytes wide on this machine. We have
also used sizeof on the constants 34 and 3.142. Do an integer and floating point constant need
4 and 8 bytes, respectively? We’ll soon find out.
If sizeof is an operator and not a function, does it need the parentheses? That depends on what
the operand is. If the operand is a fundamental type (like int and float), parentheses are necessary.
Otherwise, they are optional as shown in the two statements featuring the constants 34 and 3.142.
Note: Unlike other operators that evaluate their operands during runtime, sizeof is a compile-time
operator. The result of its operation is known to the compiler at the time of compilation, which
implies that the value is built into the executable.

5.6 THE INTEGER TYPES


An integer is a contiguous collection of digits without a decimal point but it may contain the
- prefix. For instance, 65536 and -7 are integers. C uses the int keyword to declare an integer
variable. Using the short and long qualifiers in addition, C offers a total of four types of integers,
whose abbreviated and expanded keywords are shown in the following list:
156 Computer Fundamentals & C Programming

This is how we declare two variables of type int and long:


int simply_int;
long simply_long = 50000;
The last type in the list (long long) has been available with many compilers before it was included
in C99. However, Visual Studio supports it only from their 2013 version. In the following sections,
we’ll examine these types, know their minimum sizes along with the relationship between these sizes.

Note: The - in -7 is an operator; it multiplies 7 by -1.

5.6.1 The ANSI Stipulation for Integers


The ANSI standard came into being after C applications had already penetrated the commercial
world. In its endeavor to create a standard that also protected these applications, ANSI consciously
left the sizes of these data types vague. However, it specified two rules:
A minimum size for the types:
short — 2 bytes (16 bits)
int — 2 bytes (16 bits) Modern int is 4 bytes
long — 4 bytes (32 bits)
long long — 8 bytes (64 bits)
A relationship between the types that can be represented in this form:
char <= short <= int <= long <= long long
The first rule explains why we used the words “at least” when discussing memory allocation for
variables. The second rule implies that a short cannot be smaller than char, an int cannot be
smaller than short, and so on. These two rules make it possible for int and long to have the same
size on one machine, while short and int may have the same size on another. We included the
char data type here because char is essentially an integer type even though it is almost exclusively
used to represent characters.

Takeaway: You can’t be assured of seeing a fixed size for any type, but ANSI guarantees
a minimum size. It also specifies a relationship between the sizes.

5.6.2 Signed and Unsigned Integers


By default, the four int types are signed, which means that they can handle both positive and
negative integers. This default behavior can be overridden by using the unsigned qualifier before
the keyword. For instance, the statement
unsigned int count;
allows count to store only positive integers. Because signed data types use one bit to store the sign,
the positive side of the range of a signed type is half of its unsigned type (3.3). For instance, the two
Data—Variables and Constants 157

bytes used by unsigned short can represent values between 0 and 216 - 1, i.e. 65,535. This range
changes to -32,768 to 32,767 (-215 to 215 - 1) for signed short.
Table 5.1 displays the essential attributes of these integers, their unsigned maximum values and
the format specifiers used by printf and scanf for handling them. Note that the unsigned format
specifier has the d replaced with u. Thus, %ld becomes %lu, %d becomes %u, and so forth.

TABLE 5.1 Attributes of Integers


Subtypes ANSI-mandated Unsigned printf/scanf Format
Minimum Size Maximum Value SpeciDer (signed/unsigned)
short 2 65,535 %hd / %hu
int 2 65,535 %d / %u
long 4 4,294,967,295 (4 billion) %ld / %lu
long long 8 18,446,744,073,709,551,615 %lld / %llu

5.6.3 The Specific Integer Types


The short Type Abbreviated from short int, the minimum and typical size for this type is
2 bytes. For the range it offers (-32,768 to 32,767), short is suitable for positive integral entities
like age, marks and family size. If you need an integer greater than 32,767 but less than 65,535,
use unsigned short.
The int Type This is the standard integer type that reflects the word size of your machine. Like
short, its minimum size is 2 bytes, but since most computers today are 32-bit machines, int is also
32 bits (4 bytes). A signed int can handle numbers up to 2 billion (on both +ve and -ve sides),
while the unsigned type can hold a number up to 4 billion. The popular and often misused %d
format specifier actually applies to int.
The long Type The long type (abbreviated from long int) is meant to hold numbers larger
than int. However, that has not always been the case on 32-bit systems. In most cases, int and
long are the same size—4 bytes, but on some systems you could see a long of 8 bytes. Also, most
64-bit programming models use 8-byte long integers. An 8-byte long integer can handle values in
the quintillion range.
The long long Type This type was added by C99 to the language to accommodate 64-bit integers.
On most systems that support this type, long long is 8 bytes. Since long in most 64-bit models
is already set to 8 bytes, it’s a little too early to say what the eventual size of long long would be.

Note: The basic format specifier for the signed integer data type is %d. A modifier is needed for
short (h), long (l) and long long (ll). For unsigned types, replace %d with %u with the same modifiers
as considered necessary. The complete list of these specifiers used by printf and scanf can be found
in Chapter 9.
158 Computer Fundamentals & C Programming

5.6.4 Octal and Hexadecimal Integers


Apart from decimal integers (base-10), C also supports octal (base-8) and hexadecimal (base-16)
numbers. C identifies them by a special prefix:
When a 0 (zero) prefixes a number comprising the digits 0 to 7, C interprets the number as
octal. Thus, 072 is an octal integer having the value (58)10 (7 ¥ 81 + 2 ¥ 80 = 58).
The printf/scanf duo uses %o to handle octal integers.
When a number comprising the digits 0 to 9 and A to F is preceded by 0x or 0X, C interprets
the number as hexadecimal. Thus, C understands 0x72 as a base-16 number having the value
(114)10 (7 ¥ 161 + 2 ¥ 160). Use the %x or %X format specifier to handle hex integers.
Systems programmers need to be familiar with these numbering systems because many programs
output numbers in these forms.

5.7 all_about_int.c: UNDERSTANDING INTEGERS


Program 5.3 partially summarizes our knowledge of the four integer data types. It demonstrates
how printf converts data across the different numbering systems. The program also shows the
consequences of data overflow and how use of wrong format specifiers affect the conversion.
Two of the variables are signed and one is unsigned. Since short_x is signed, it uses 15 bits, so 32767
is the maximum value it can hold. This is confirmed by the octal representation which shows the
largest octal digit (7) occupying all five slots. The right format specifiers have been used in the first
three printf statements.
Next, we create an overflow situation by adding 1 to short_x. Signed short has a range of -32,768
to 32,767. The number 32768 resulting from the addition exceeds the maximum positive value
by 1. This causes an overflow which sets the sign bit to 1 and the remaining bits to 0. The resultant
number (1000 0000 0000 0000)2 represents the minimum value for a signed short (-32768).

HOW IT WORKS: Which Integer Type to Choose?


The ANSI stipulation leads to overlap of the type sizes. Some machines have the same size
for short and int, or int and long. How does one then determine the right type to use for an
integer? If your application needs to be portable, the following guideline should help:
If you need 2 bytes and both short and int have this size on your machine, then use short. Your
program won’t waste memory when it is moved to a 32-bit machine where int expands to 4 bytes.
However, if you need 4 bytes, and both int and long have this size, then use long because int can
reduce to 2 bytes when the application moves to a smaller machine.
The above rule of thumb will protect your application but it is not guaranteed to prevent
wastage. If you use long because of the guarantee of getting 4 bytes, then be prepared to get
8 bytes instead on 64-bit machines.
Data—Variables and Constants 159

The last two printf statements use wrong format specifiers. We first printed a negative integer with
%u, the specifier meant for unsigned int. We then printed the new value of int_x (123456) with %hd,
the specifier meant for short. This number also exceeds the maximum value that can be handled
by short. Both statements output junk values.

Note: The last two printf statements use symbols like %%d and %%u to print the strings %d and %u,
respectively. Because % is interpreted in a special manner (the specifier prefix) by printf, the only
way to literally print a % is to prefix it with another.

/* all_about_int.c: Assigns and displays integer values.


Shows effects of overflow and wrong format specifier. */
#include <stdio.h>

int main(void)
{
short short_x = 32767; /* Signed -- uses 15 bits */
int int_x = -65; /* Signed -- uses 31 bits */
unsigned long long_x = 400000; /* Unsigned -- uses 32 bits */

printf(“short_x: Decimal: %hd, Octal: %ho, Hex: %hx\n”,


short_x, short_x, short_x);
printf(“int_x: %d\n”, int_x);
printf(“long_x = %lu\n”, long_x);

/* Data too large for type -- causes overflow */


short_x = short_x + 1; /* 16-bit value can’t be held in 15 bits */
printf(“short_x after incrementing: %hd\n”, short_x);

/* Wrong selection of format specifier */


printf(“int_x printed with %%u = %u\n”, int_x);
int_x = 123456;
printf(“int_x printed with %%d = %d, with %%hd = %hd\n”, int_x, int_x);

return 0;
}

PROGRAM 5.3: all_about_int.c


short_x: Decimal: 32767, Octal: 77777, Hex: 7fff
int_x: -65
long_x = 400000
short_x after incrementing: -32768
int_x printed with %u = 4294967231
int_x printed with %d = 123456, with %hd = -7616

PROGRAM OUTPUT: all_about_int.c


5.11 char: THE CHARACTER TYPE
C supports the char data type which is just wide enough to hold the values of the computer’s character
set. Our PCs use the 8-bit ASCII character set, so char by definition is one byte wide. Even though char
is essentially an integer type, we are discussing it separately for two reasons:

164 Computer Fundamentals & C Programming

The numeric value stored in a char variable can easily be displayed in its equivalent character
form and vice versa.
Strings owe their origin to char. A string is simply an array of type char.
The first point implies that the value 65 stored as a char can be printed as A by printf using %c.
Conversely, the character A input from the keyboard can be saved in a char variable as 65 by scanf
using %c. Data of type char can also be represented in two other ways:
By a character enclosed within single quotes (e.g. ‘A’).
By a special escape sequence like ‘\c’, but only a handful of non-printable characters can be
represented by an escape sequence.
Like the other integer types, char occurs in signed and unsigned forms. The signed form can hold
values between -128 to 127, while the range for the unsigned type is 0 to 255. Unlike the other
integer types, which are signed by default, the default type of char is system-dependent. So, if you
need to store an 8-bit ASCII character, specify the data type explicitly as unsigned char.

5.11.1 char as Integer and Character


If char is unsigned, then every character of the 8-bit ASCII set can be assigned to a char
variable. The following examples demonstrate that any integer type can be used to display
a character with %c. The last statement should sound a beep as the BEEP character ([Control-g])
has the ASCII value 7:

char let1 = ‘A’; short let2 = 97; char let3 = ‘9’; int let4 = 7;
Code Output
printf(“‘%c’ = %d\n”, let1, let1); ‘A’ = 65
printf(“‘%c’ = %d\n”, let2, let2); ‘a’ = 97
printf(“‘%c’ = %d\n”, let3, let3); ‘9’ = 57
printf(“‘%c’ = %d\n”, let4, let4); ‘’ = 7
The number 7 is stored in memory as (0000 0111)2. But ‘7’ is a character, whose ASCII value (55)
is stored as (0011 0111)2. When you input 7 from the keyboard in response to a scanf call, it’s ‘7’
that is sent to the keyboard buffer (4.8.2—How It Works). The %d specifier tells scanf to convert
‘7’ to 7 (if the intent was to use an integer).
You can use octal and hexadecimal numbers too. Simply precede the number with 0 and 0x,
respectively:
char c = 0101; 101 = 1 ¥ 82 + 0 + 1 ¥ 80 = 65
char c = 0x41; 41 = 4 ¥ 161 + 1 ¥ 160 = 65
Beginners often make the mistake of using double quotes (“A” instead of ‘A’). Double-quoted
characters (like “A” or “Internet of things”) represent a string, which is not a fundamental data
type. C understands a string as an array (a derived data type) of type char, but more of that later.
Data—Variables and Constants 165

Note: In C, ‘A’ is different from “A”. The former is a character and the latter is a string. This string
has two characters (and not one), which may puzzle you now but not after you have read
Section 5.15.

5.11.2 Computation with char


Since any single-quoted character actually represents an integer, it can be used in arithmetic
operations. The following assignments are all valid, but the third one has a message to convey:
char letB, letD, leta, digit4;
letB = ‘A’ + 1; letB is now ‘B’
letD = letB + 2; letD is now ‘D’
leta = ‘A’ + 32; leta is now ‘a’ but only in ASCII
digit4 = ‘0’ + 4; digit4 is now ‘4’
Should we use ‘A’ or 65, its ASCII value? The question is significant because on EBCDIC
systems (designed by IBM), ‘A’ evaluates to 193 and not 65. If you have portability in mind, use
the character rather than the numeric representation.

Note: Because letters and numerals occur in a contiguous sequence (but in separate blocks) in the
ASCII list, it’s convenient to sort words and lines containing these characters. A simple while loop
can print a list of all letters or numerals.

5.11.3 char as Escape Sequence


C supports the escape sequence as a synonym for a non-printable character. An escape sequence is
a two-character combination comprising a \ (backslash) and a lowercase letter. The \ forces the
character following it to be treated specially. From Table 5.3, we observe that \t (the tab) expands
to eight spaces on your terminal. The newline (\n) is universally used in printf to advance the
cursor to the next line. The \f initiates a page break.
But what about those non-printable characters that can’t be represented by an escape sequence?
You need not worry because the \ can also be used with the ASCII value of any character provided
this value is represented in octal or hexadecimal. Simply prefix the octal value with a \ and the
hex value with \x:
char bell = ‘\7’; \07 not permitted
char bell = ‘\x7’; \0x7 not permitted
printf(“Wake up\7”); Terminal beeps
The characters ‘, “ and \ are printable, but they play a special role in framing strings and character
constants. So how does one represent them literally, i.e., without their special meaning? Escape
them with a \ wherever necessary:
char single_quote = ‘\’’, backslash = ‘\\’;
printf(“\\ and \” are special\n” ); Prints \ and “ are special
This mechanism, however, doesn’t work with the % prefix of the format specifier. You can’t print %
using \%; you need to use %%.
166 Computer Fundamentals & C Programming

Note: You can use a leading zero to represent an octal or hexadecimal character, but only when the
character is embedded in a string. Thus, “Wake up\a”, “Wake up\07” and “Wake up\7” are equivalent.

TABLE 5.3 Escape Sequences


Escape Sequence SigniDcance
\a Bell
\b Backspace
\c No newline (cursor in same line)
\f Formfeed
\n Newline
\r Carriage return
\t Tab
\v Vertical tab
\\ Backslash
\n ASCII character represented by the octal value n, where n can’t exceed
0377 (decimal value 255)
\xn ASCII character represented by the hex value n, where n can’t exceed 0xFF
(decimal value 255)

5.12 all_about_char.c: UNDERSTANDING THE char DATA TYPE


Let’s now use our knowledge of char in Program 5.5. This program first establishes whether the char
type is signed or unsigned on our machine. It then uses two while loops to print all the uppercase
letters and numerals in a portable manner. We’ll also learn to use some of the escape sequences.
Observe that even though the variable schar was assigned 250, it shows up as -6, which means that
the MSB (most significant bit) for schar is set to 1. However, the value of unsigned uchar shows
up correctly. It’s clear that this system uses signed char by default, which means the maximum
positive value it can handle is 127. Note that these char variables were assigned integers and also
printed as integers without any problem.
The ASCII list in Appendix B shows letters and numerals arranged sequentially. A while loop
thus becomes an automatic choice for printing all of them. For the first loop, the variable letter
moves from ‘A’ to ‘Z’ before the loop terminates. For the second loop, numeral moves in reverse
sequence from ‘9’ to ‘0’. Using ‘A’, ‘Z’, ‘0’ and ‘9’ instead of their ASCII equivalents makes
the code portable across all systems.
The space after %c and the absence of \n in printf ensures that the elements are printed in a single
line and separated by a space. We have used a different technique this time of incrementing and
decrementing a variable (with ++ and --).
Data—Variables and Constants 167

/* all_about_char.c: Handling the char data type and escape sequences. */

#include <stdio.h>

int main(void)
{
char schar = 250;
unsigned char uchar = 250;
char letter = ‘A’; int numeral = ‘9’;

printf(“schar = %d\n”, schar); /* Know char default on your system */


printf(“uchar = %d\n”, uchar);

/* Printing the entire alphabet in uppercase */


while (letter <= ‘Z’) { /* Using ‘Z’ instead of its ASCII value */
printf(“%c “, letter); /* makes the code portable. */
letter++; /* This is letter = letter + 1 */
}
printf(“\n\t\tNext line starts after 16 spaces and also beeps\07\n”);
/* Printing all digits of the base-10 system in reverse */
while (numeral >= ‘0’) { /* This code is also portable because */
printf(“%c “, numeral); /* ‘0’ is used instead of ASCII value. */
numeral--; /* This is numeral = numeral - 1 */
}
return 0;
}
PROGRAM 5.5: all_about_char.c
schar = -6 Machine uses signed char by default
uchar = 250
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Next line starts after 16 spaces and also beeps
9 8 7 6 5 4 3 2 1 0

PROGRAM OUTPUT: all_about_char.c

Between the two loops, a printf statement demonstrates the use of the \ and escape sequences.
A \t shifts the cursor by eight spaces, so using two of them offsets the line by 16 spaces. If your
terminal supports the beep sound, you’ll hear a beep as well (\07).

Note: You must know the distinction between ‘A’, ‘APE’, “A” and “APE”. ‘A’ is a character constant
whose ASCII value (65) is stored as a binary number. ‘APE’ is not a valid character constant because
only the last character (‘E’) is stored. “A” and “APE” are legal strings, occupying two and four
characters in memory, respectively. Why an extra character for the strings? Read on.
168 Computer Fundamentals & C Programming

5.13 DATA TYPES OF CONSTANTS


So far, our focus has been on variables. We are yet to know what the data types of constants are.
Is 32 an int or a short? Is ‘B’ stored as a char or an int? Is 5.5 treated as a float or a double?
We need to know the answers to all of these questions before we take up expressions in Chapter 6.
Constants represent data that don’t change in the program. They also have types even though they
are not formally declared. Using a set of rules, the compiler detects the data type of constants by
simple examination. C supports a wide range of constants—both unnamed and named—which
may be grouped into the following categories:
Unnamed constants belonging to the fundamental data types. The constants 12, -34.5 and
‘A’ belong to this category.
Named or symbolic constants that are defined with #define at the beginning of the program.
You have seen one of them used in Program 4.1.
Variables that behave like constants when the const qualifier is added to the variable declaration.
String constants like “Nougat is here” belong to the derived data type.
C specifies a default type for a constant, but this default is often changed by the compiler or the
programmer. This can happen for the following reasons:
The size of the constant is too large to be accommodated in the default type. For instance,
the number 5000000000 is too large for an int, the default type for integer constants.
You may like to force the constant to have a different type.
The type can automatically change when a constant occurs in an expression where at least one
component has a higher type.
In the following sections, we’ll examine all of the constant types except string constants, discussions
on which are postponed to Chapter 13.

5.13.1 Constants of the Fundamental Types


Integer Constants C specifies int (signed) as the default type for an integer constant.
The compiler treats 655 as an int even though the number easily fits in a short. If a constant
doesn’t fit in its default type, the compiler tries the next higher type using the following sequence:
int unsigned int long unsigned long long long unsigned long long
When a constant is used in an expression, we may sometimes want it to have a specific type
(coercion). C offers special symbols that can be suffixed to these constants:
U or u— unsigned int
L or l— signed long
UL or ul— unsigned long
LL or ll— long long
ULL or ull— unsigned long long
Data—Variables and Constants 169

For instance, the compiler, which otherwise sees 3 as an int, treats 3L as a long. There are no
constants of type short. Next time you see the constants 540U or 450L, remember to treat them as
unsigned int and signed long, respectively.
Floating Point Constants C treats a real constant as a double which takes up 8 bytes on most
systems. Constants like 3.142 and 1789.456 have the type double even though a float (4 bytes) is
wide enough for them. However, unlike integer constants which can only be promoted to long and
long long, real constants can be both promoted and demoted:

The F or f suffix demotes a real constant to float. The constants 3.142f and 1789.456F have
the type float.
The L or l (el) suffix promotes a real constant to long double, which may take up to 16 bytes.
These suffixes work with the exponential form also (like 3E4F). Demoting a constant to float often
makes sense because it leads to savings in memory space and computation time.
Character Constants Any character or escape sequence enclosed in single quotes represents
a character constant. The characters ‘A’, ‘\t’ and ‘\7’ are character constants having the ASCII
values 65, 11 and 7, respectively. Strangely enough, C treats character constants as type int rather
than char. This means the constant ‘A’ is stored in a space of 4 bytes (size of int).

Takeaway: The default type for an integer constant is int. For a real number, the default
is double. A character constant is stored as an int. C doesn’t support a constant of
type short.

5.13.2 Variables as Constants: The const Qualifier


A constant that is used multiple times in a program is often stored as a variable. If the value is
changed later, then the code needs to be modified at a single location, thus making maintenance
easier. The constant itself should be stored as a variable using the const qualifier:
const float euro_dollar = 1.325;

The const qualifier protects a variable from being rewritten by marking it read-only. You’ll often
see this qualifier in the syntax of many C string-handling functions.

5.13.3 Symbolic Constants: Constants with Names


Storing a constant in a variable has a drawback. Every time a variable is encountered, the program
has to access its memory location. Memory access takes time, so C offers symbolic constants to avoid
this access. We have used PI as a symbolic constant in a #define directive of the preprocessor (4.4).
The euro-dollar conversion problem has a better solution in the following #define directive:
#define euro_dollar 1.325f No semicolon — not a C statement
The preprocessor replaces euro_dollar with 1.325 everywhere in the program. The compiler thus
never gets to see euro_dollar but sees 1.325 instead. (The f suffix ensures that the default double
170 Computer Fundamentals & C Programming

is not used for the constant.) If the value of a constant doesn’t change elsewhere in the program or
when the program is running, why use a variable at all? However, the topic appropriately belongs
to Chapter 17 which discusses preprocessor directives.

Tip: If you need to use a constant repeatedly in a program, either define it as a symbolic constant
or declare it as a variable using the const qualifier.

5.14 sizeof_constants.c: PROGRAM TO EVALUATE SIZE OF CONSTANTS


If you are concerned about possible misuse of memory, you must know the size of the constants
you use in your programs. We have used the sizeof operator with types (5.5), so let’s now use
Program 5.6 to repeat the exercise with constants.
The program first lists the size of every fundamental type supported on this machine to enable
their comparison with constants. Observe that the constant 34 occupies 4 bytes, the size of an int.
We know that a char constant is stored as an int too, so the constant ‘A’ occupies 4 bytes. However,
the char variable storing this constant uses just 1 byte.

/* sizeof_constants.c: Evaluates the size in bytes of constants. Also shows


the effect of the L and F suffixes on size. */
#define LOOP_VAR 50
#include <stdio.h>

int main(void)
{
char letter = ‘A’;
printf(“Size in bytes of the fundamental data types on this machine:\n”);
printf(“int: %d, long: %d, float: %d, double:% d, long double: %d\n”,
sizeof(int), sizeof(long), sizeof(float),
sizeof(double), sizeof(long double));
printf(“\nSize of constants on this machine:\n”);
printf(“Size of 34 = %d bytes\n”, sizeof 34);
printf(“Size of ‘A’ = %d bytes\n”, sizeof ‘A’);
printf(“Size of letter which is set to ‘A’ = %d byte\n”, sizeof letter);
printf(“Size of 34L = %d bytes\n”, sizeof 34L);
printf(“Size of 3.142 = %d bytes\n”, sizeof 3.142);
printf(“Size of 3.142F = %d bytes\n”, sizeof(3.142F));
printf(“Size of 3.142L = %d bytes\n”, sizeof(3.142L));
printf(“Size of LOOP_VAR = %d bytes\n”, sizeof(LOOP_VAR));

/* Note that the size of this string constant is NOT 8! */


printf(“Size of string \”Lollipop\” = %d bytes\n”, sizeof(“Lollipop”));
return 0;
}

PROGRAM 5.6: sizeof_constants.c


Data—Variables and Constants 171

Size in bytes of the fundamental data types on this machine:


int: 4, long: 4, float: 4, double: 8, long double: 12

Size of constants on this machine:


Size of 34 = 4 bytes
Size of ‘A’ = 4 bytes
Size of letter which is set to ‘A’ = 1 byte
Size of 34L = 4 bytes
Size of 3.142 = 8 bytes
Size of 3.142F = 4 bytes
Size of 3.142L = 12 bytes
Size of LOOP_VAR = 4 bytes
Size of string “Lollipop” = 9 bytes
PROGRAM OUTPUT: sizeof_constants.c
Let’s now examine the effect of suffixing the constants. Both 34 and 34L take up 4 bytes because
int and long have the same size on this machine. The program also confirms that the real number
3.142 is stored as a double by default (8 bytes), as a float (4 bytes) when the F suffix is used, and as
a long double (12 bytes) when the L suffix is used.
The last two printf statements show how sizeof evaluates constants not belonging to the
fundamental types. sizeof doesn’t see the symbolic constant LOOP_VAR but sees the number 50,
which is an integer constant. Also note that the string “Lollipop” takes up 9 and not 8 characters.
We’ll finally explain this anomaly in the next section.

5.15 ARRAYS AND STRINGS: AN INTRODUCTION


We often have to deal with a group of related data. For instance, we may need to store the monthly
rainfall for an entire year. Rather than create 12 variables, we use an array to store the 12 values.
An array is a collection of data items belonging to a single type. We need to know some of the basics
of arrays and strings now because they feature in bits and pieces in subsequent chapters. However,
arrays are discussed in detail in Chapter 10, so you won't lose much by skipping this section.
The following statement declares an array having the name month. It is capable of holding
12 values, each of type int:
int month[12]; Uninitialized array
Assuming a 4-byte int, the compiler uses this declaration or definition to allocate a contiguous
chunk of 48 bytes of memory. Each element of the array is accessed by the notation month[n], where
n is an integer representing the index. The value of n here lies between 0 and 11. The first element
is accessed as month[0] and the last element is month[11].
At this stage, the array elements are uninitialized, which means that they have junk values.
You can perform the initialization at the time of declaration by assigning a set of comma-delimited
values enclosed by curly braces:
int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
6 Operators and Expressions
Sections—6.1-6.5 and 6.10-6.16

WHAT TO LEARN

Attributes of operators and their role in evaluating expressions.


Significance of the arithmetic operators.
How data types of variables and constants are implicitly changed.
Role of casts in explicit conversion of data types.
Using operator precedence and associativity in evaluating expressions.
Significance of the assignment operators (+=, -=, *=, etc.).
The increment (++) and decrement (--) operators and their side effects.
Boolean evaluation of expressions using relational and logical operators.
Use of the conditional and sizeof operators.

6.1 EXPRESSIONS
Variables and constants represent data in their most elemental form. These data objects become
useful when combined with one another to form expressions. The components of an expression
are linked to one another by symbols called operators. For instance, a + b / 7 is an expression
where + and / are operators. The expression has a value which here is evaluated arithmetically.
An arithmetic expression is not the only type of expression you’ll encounter in C. There are other
types—like relational and logical expressions. For instance, a > b is not an arithmetic expression
but it still has a value. The following examples illustrate the diversity of expressions used in C:
C Construct Expression
interest = principal * rate / 100; principal * rate / 100 (arithmetic)
if (age > 18) age > 18 (relational)
while (answer == ‘y’) answer == ‘y’ (relational)
if (age > 15 && age < 25) age > 15 && age < 25 (logical)
(Continued)
Operators and Expressions 179

(Continued)
C Construct Expression
while (count-- >= 0) count-- >= 0 (relational)
answer = ‘y’; answer = ‘y’ (assignment; also an expression)
++count; ++count (expression without operators)
if (count) count (Ditto; simplest expression)
Except for the last one, all of these expressions use one or more operators. The simplest expression,
however, uses no operators. This means that the variable count and the constant 5 are two valid
expressions. This all-embracing view of expressions may seem trivial but it has important
consequences, as you will gradually discover.
Every C expression has a value and the significance of this value depends on the nature of the
expression. For an arithmetic expression (like a + b) it’s the computed value that interests us.
But relational expressions (like age > 18) evaluate to either of two boolean values, which we
interpret as true (value > 0) or false (value = 0).

6.2 OPERATORS
Operators form the connecting link between variables and constants in expressions. An operator
acts on one or more operands. For instance, in the expression x - 5, the - symbol is an operator,
which acts on the operands x and 5. C supports a rich collection of operators—richer than any
language known previously. These operators can be grouped into the following types:
Arithmetic operators (like +, -, etc.)
Assignment operators (like =, +=, -=, *=, etc.)
Increment and decrement operators (++ and --)
Relational operators (like >, <, <=, etc.)
Logical operators (&&, || and !)
Bitwise operators (like &, |, >>, <<, etc.)
The comma, sizeof and cast operators
An operator may be unary, binary or ternary. A unary operator has one operand. For instance,
the - symbol in the negative constant -5 is a unary operator which multiplies its operand by -1.
(The unary - is different from the binary - which performs subtraction.) The ++ in count++ is
a unary operator which increments its operand by one.
Most operators are of the binary type which needs two operands. The assignment operator = in x = 5
is a binary operator. Arithmetic operators like * and / and relational operators like > and < are also
binary operators. C has one ternary operator which uses the ? and : symbols.
When an expression contains multiple operators, the order of their evaluation becomes important.
For instance, when evaluating the expression x + y * z, does multiplication occur before addition?
For this purpose, C specifies two attributes for each operator:
180 Computer Fundamentals & C Programming
The precedence (priority) ensures that a high-priority operator is acted upon before
a low-priority one. In the expression x + y * z, the * has a higher precedence than +, so
multiplication will be done first.
The associativity determines the direction of evaluation (left-to-right or right-to-left). In the
expression x * y / z, it is associativity and not precedence that determines that multiplication
will be done first. You may think that it doesn’t matter, but if y and z are integers, then it
definitely matters (6.10.3).
These two attributes will be examined in detail later in the chapter. It’s important that you understand
them completely because a little learning in this area would certainly be a dangerous thing.
Note: The constant 5 is an expression but 5; is a valid statement. Since many functions return a
value, they can also be considered as expressions. The printf function returns a value and so can
be used anywhere an expression is used. Our first program, expressions.c, clearly demonstrates this.

6.3 expressions.c: EVALUATING EXPRESSIONS


Program 6.1 features a set of printf statements that print the values of expressions. One of these
expressions is printf itself. Finally, the program executes a variable and constant as statements.
Because these statements have no effect on the program, the compiler may generate warnings that
may be ignored.
Let’s examine the five printf statements that follow the initial one. The first four of them use
binary operators, but the last one uses a unary one. For identifying the statements, we’ll use the
second argument to printf as the subhead in the following discussions:
distance/mileage This is a simple arithmetic expression whose value is obtained by floating-
point division. The %.2f specifier overrides the default decimal width of 6.
mileage = 20 This expression also has a separate value, which is the value of the operand
on the right side of =. So, both mileage and the expression mileage = 20
have the value 20.
answer == ‘y’ The == operator checks for equality by comparing answer to ‘y’. This is
a relational expression that evaluates to either 0 (false) or 1 (true). It is
obviously false.
answer = ‘y’ Unlike the previous expression, this expression is an assignment and has
the value ‘y’, which in ASCII is 121.
++mileage The ++ unary operator increments a variable so printf outputs the value 21.
A possible jolt could come from the last printf statement which contains another printf as its
second argument but without the semicolon. Observe that both printf functions in the statement have
been executed, while the value 10 is also printed. You’ll soon know that printf can be interpreted
as an expression, whose return value (10) is the number of characters printed (9 from “Honeycomb”
and 1 from \n).
The last two statements contain harmless expressions that are executed as statements. These
expressions evaluate to 21 and 420, respectively. They are valid C statements even if they do nothing.
Operators and Expressions 181

/* expressions.c: Evaluates value of expressions. Establishes that


an assignment or function like printf is also an expression. */
#include <stdio.h>
int main(void)
{
int mileage = 17; float distance = 420; char answer = ‘n’;
printf(“Printing value of expressions:\n”);
printf(“distance / mileage has the value %.2f\n”, distance / mileage);
printf(“mileage = 20 has the value %d\n”, mileage = 20);
printf(“answer == ‘y’ has the value %d\n”, answer == ‘y’);
printf(“answer = ‘y’ has the value %d\n”, answer = ‘y’);
printf(“++mileage has the value %d\n”, ++mileage);
/* Surprise! printf is also an expression. */
printf(“This printf expression has the value %d\n”, printf(“Honeycomb\n”));
/* Expressions as statements */
mileage; /* Perfectly valid statement */
420; /* Same, so ignore the warnings */
return 0;
}

PROGRAM 6.1: expressions.c


Printing value of expressions:
distance / mileage has the value 24.71
mileage = 20 has the value 20
answer == ‘y’ has the value 0
answer = ‘y’ has the value 121 ASCII value of ‘y’
++mileage has the value 21
Honeycomb
This printf expression has the value 10

PROGRAM OUTPUT: expressions.c

Takeaway: An expression which is not a function evaluates to either an absolute or a boolean


value (0 or 1). For a function, its evaluated value is the value returned by it on execution.
However, the printf function not only returns a value but also has the side effect of printing
something. It is strange that printing, the main task of printf, is “relegated” to a “side effect!”

6.4 ARITHMETIC OPERATORS


We begin our discussion on the individual operators with the five arithmetic ones:
+, -, *, / and % (Table 6.1). Your electronic calculator uses the first four in identical manner.
The %, known as the modulus operator, is used in C for computing the remainder of an integer
division. C permits some flexibility in arithmetic operations, but it places some restrictions as well.
182 Computer Fundamentals & C Programming

6.4.1 The +, -, * and /: The Four Basic Operators


The operators + and - are meant to do what everyone knows. Multiplication and division are
handled by * and /. These four binary operators support a mix of integer and floating point operands
which must have the right data types. Here are some situations that can cause problems:
256 * 256 Value is 65536; can’t hold in a short
22 / 7 Value is 3 not 3.142!
22.0 / 7 Value is 3.142857; 7 converted to 7.0
5 / 0 Program aborts; division by zero not permitted
As evident from the first example, multiplication is the most common source of data overflow
problems. The second example is an eye-opener; it evaluates to 3 because integer division leads to
truncation of the decimal portion. The next example shows that an expression is evaluated using
floating point arithmetic only if at least one operand is of the floating point type. Finally, your
programs must have adequate checks to ensure that the denominator is never zero.

6.4.2 The %: The Modulus Operator


The other arithmetic operator we consider is the %, known as the modulus operator. This operator
evaluates the remainder of a division between two integers. The expression 10 % 7 (10 modulo 7)
evaluates to 3. Consider a few more examples:
55 % 2 Value is 1; suitable for odd or even number check
21 % 7 Value is 0; suitable for day of the week check
5 % 8.0 Error; only integers allowed
-14 % 5 Implementation dependent; usually evaluates to -4
As seen from the third example, the operation fails if one of the operands is a real number. The %
provides a simple solution to some common tasks like checking for even or odd numbers and
determining whether a year is a leap year or not.
TABLE 6.1 The Arithmetic Operators
Operator Signi cance
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulus (integers only)

6.5 computation.c: MAKING SIMPLE CALCULATIONS


Let’s use the arithmetic operators in our next program (Program 6.2). The program takes as input
an even number from the user and computes the sum of all integers from 1 to the value that is
input. The computation uses an interesting technique that is documented in the program itself.
A validation check ensures that you don’t key in odd integers. The program is invoked three times
but with only one successful outcome.
Operators and Expressions 183

/* computation.c: Uses arithmetic operators to compute the sum of the


first n integers where n is even. Quits program if n is not even. */
#include <stdio.h>
#define FIRST_NUMBER 1
int main(void)
{
int last_number, pairs, result, sum_pair;
printf(“Enter the last number: “);
scanf(“%d”, &last_number);
if ((last_number % 2) == 1) { /* Can’t do without % */
printf(“Not an even number\n”);
return 0; /* Terminates program */
}
/* Example: For calculating sum of first 100 integers, first divide the
integers into pairs. Sum of 1 and 100 = 101, sum of 2 and 99 = 102, etc.
Final sum = Sum of each pair * number of pairs */
sum_pair = FIRST_NUMBER + last_number;
pairs = sum_pair / 2;
result = pairs * sum_pair;
printf(“Sum of first %d integers = %d\n”, last_number, result);
printf(“Difference between first and last integer = %d\n”,
last_number - FIRST_NUMBER);
return 0;
}

PROGRAM 6.2: computation.c


Enter the last number: 5
Not an even number
Enter the last number: 100
Sum of first 100 integers = 5050
Difference between first and last integer = 99
Enter the last number: -6
Sum of first -6 integers = 10 Meaningless
Difference between first and last integer = -7 This is correct

PROGRAM OUTPUT: computation.c

A symbolic constant (FIRST_NUMBER) makes an appearance in this program. The preprocessor


replaces FIRST_NUMBER with 1 at the two locations of its occurrence in the program. You can change
the starting point by changing this value. Note that we can’t check whether a number is even
or odd without the % operator. The program documents the logic behind the algebraic formula
sum = n ¥ (n + 1) / 2. The validation check rejects odd integers but it is not good enough to
prevent the input of negative and floating point numbers.
Note: While integer division causes truncation of the fractional component, the truncated portion
can be obtained by using the % operator. You then need two operations to get both quotient and remainder.
188 Computer Fundamentals & C Programming

6.10 ORDER OF EVALUATION


Proper use of data types and conversion techniques are not enough to evaluate expressions
correctly. You also need to use a specified order of evaluation. You probably remember the “BODMAS”
acronym from your school days, which specified that division is performed before addition (D before
A in BODMAS). C too has a set of rules for all the 40 operators of the language. These rules, which
we’ll now discuss, are based on two operator attributes: precedence and associativity.

6.10.1 Operator Precedence


Every operator has a precedence which signifies the priority of its operation. The precedence
is often interpreted in this book in terms of levels. An operator belonging to a higher level has
a greater precedence or priority than a lower-level operator. The levels for some of the operators
that we have already discussed can be represented in the following manner:
Level 1 — ( and )
Level 2 — + (unary), - (unary)
Level 3 — sizeof
Level 4 — (cast)
Level 5 — *, / and %
Level 6 — + (binary) and - (binary)
Level 7 — =
The * and / operators belong to a higher level than + and -. So, the expression 32 + 9 / 5 is
evaluated by performing the division before the addition. Note that the cast operator has a lower
precedence than sizeof but a higher precedence than the arithmetic operators. The complete table
of operator precedence has over 15 levels (Table 6.2 and Appendix A), but the above data collated
from the table is adequate to explain how C handles the following expression:
fahrenheit = 32 + -40.0 * 9 / 5;

This represents the well-known formula that converts temperatures between Celsius and Fahrenheit.
The constant -40.0 represents the temperature in Celsius. This is how C evaluates the expression:
1. Among these operators, the unary operator, -, has the highest level, so C first multiplies 40.0 with -1.
2. The sub-expression, -40.0 * 9 / 5 is then evaluated because the * and / have a higher priority
than the binary +. Here, we have two operators belonging to the same level. For reasons of
associativity that will be explained later, C performs the multiplication before the division, but
before doing so, it promotes the type of 9 to double (the type of -40.0).
3. The sub-expression at this stage has the value -360.0 which has to be divided by 5. C now
promotes the type of 5 (by default, int) to double also. Division of -360.0 by 5.0 yields -72.0.
4. C now performs the addition operation, but only after promoting the type of 32 to double. The
final result is 32.0 + -72.0 = -40.0.
Operators and Expressions 189

5. The = operator has the lowest precedence in our list, so the result of the previous operation is
finally assigned to fahrenheit. If fahrenheit is of type double, the right-hand side needs no
conversion, but if it is a float, then the expression will be converted to float before assignment.
This could lead to truncation but not with the value that we are working with.

6.10.2 Using Parentheses to Change Default Order


Our abridged list of levels shows the parentheses having the highest precedence. Thus, their use
at any location changes the default order of evaluation at that location. If we were to rework the
previous equation to convert a given temperature in Fahrenheit (say, -40F) to Celsius, we need to
use parentheses:
celsius = (-40 - 32) * 5 / 9

Here, the expression -40 - 32 is evaluated first (but after multiplying 40 by -1). In the absence
of parentheses, -32 would have been multiplied by 5 first. This would have resulted in erroneous
evaluation.
Note: The expression -40 - 32 needs two operations for evaluation and not one. The first - is the
unary minus which multiplies 40 by -1. 32 is next subtracted from the result. However, C treats
-40 -32 and -40 - 32 as equivalent.

6.10.3 Operator Associativity


Every operator has an associativity which determines the direction of evaluation when an operand
is shared by two operators having the same precedence. This attribute can take on two values—
right-to-left (R-L) or left-to-right (L-R). To understand the implications of this property, consider the
following statements that use the same operators and operands but arranged in a different sequence:
printf(“%.2f\n”, -40.0 * 9 / 5 + 32); Prints -40.00
printf(“%.2f\n”, 9 / 5 * -40.0 + 32); Prints -8.00
The first statement prints the correct value. Here, the * and / have the same precedence and also
share an operand (9). From Table 6.2, it is seen that * and / have left-to-right associativity. This
means that the operand 9 will be operated upon from the left. Thus, multiplication will be done
first, followed by division.
But why does the second printf output a different result? Here, it’s the 5 that is shared by the
same operators, / and *. Because of L—R associativity, division occurs first, but this also leads
to truncation. So the final result is 1 ¥ -40.0 + 32 = -8.0. However, we could have prevented
truncation by using the cast (float) 9.
Now comes the inevitable question that has often been answered incorrectly. How is the expression
a * b + c / d evaluated where the equal-priority operators, * and /, don’t share an operand?
All we can say is that the addition will be done last. The outcome is implementation-dependent
even though the final result won’t be affected. Associativity has no role to play here.
190 Computer Fundamentals & C Programming

Takeaway: Operator associativity becomes a determining factor only when two operators
having the same precedence share an operand. When equal-priority operators don’t share an
operand, the outcome is implementation-dependent but it won’t lead to an error. An exception
can be made when using the logical operators (6.16).

Note: Table 6.2 lists all operators featured in the 17 chapters of this book. The bitwise operators
(discussed in Chapter 17) are included in the complete version of the table (Appendix A).

TABLE 6.2 Operator Precedence and Associativity (Complete table in Appendix A)


Operator Signi cance Associativity
( ) Function call
[ ] Array subscript
., -> L-R
Member selection (for structures)
++, -- Postfix increment/decrement
++, -- Prefix increment/decrement
+, - Unary
! Logical NOT
R-L
sizeof Size of data or type
(data type) Cast
*, & Dereference/address (for pointers)
*, /, % Arithmetic L-R
+, - Arithmetic (binary) L-R
<, <=, >, >= Relational L-R
==, != Relational L-R
&& Logical AND L-R
|| Logical OR L-R
? : Conditional (ternary) R-L
=, +=, -=,
Assignment R-L
*=, /=, %=
, Comma (expression delimiter) L-R

6.11 order_of_evaluation.c: PRECEDENCE AND ASSOCIATIVITY


Our next program (Program 6.5) confirms the observations made on operator precedence and
associativity in the preceding sections. The first part shows what the relative precedence of six
operators are and how they can be changed using parentheses. The second part represents an
excellent programming tip related to associativity.
Operators and Expressions 191

/* order_of_evaluation.c: Demonstrates precedence and associativity. */


#include <stdio.h>
int main(void)
{
float f, celsius;
short year = 2014, years_left;
/* 1. Precedence issues */
f = 7 * 8 + 9 - 16 / 4 ; /* 56 + 9 - 4 = 61 */
printf(“1. %.2f\n”, f);
f = 7 * (8 + 9) - 16 / 4 ; /* 7 * 17 - 4 = 115 */
printf(“2. %.2f\n”, f);
years_left = 4 - year % 4; /* No parentheses required */
printf(“3. years_left = %hd\n”, years_left);
/* 2. Associativity issues */
printf(“4. Enter temperature in Celsius: “);
scanf(“%f”, &celsius);
printf(“5. %.2fC = %.2fF (Correct)\n”, celsius, celsius * 9 / 5 + 32);
printf(“6. %.2fC = %.2fF (Incorrect)\n”, celsius, 9 / 5 * celsius + 32);
return 0;
}

PROGRAM 6.5: order_of_evaluation.c


1. 61.00
2. 115.00
3. years_left = 2
4. Enter temperature in Celsius: 40
5. 40.00C = 104.00F (Correct)
6. 40.00C = 72.00F (Incorrect)

PROGRAM OUTPUT: order_of_evaluation.c

Part 1 This part features three simple expressions that use the arithmetic operators. It’s now
a given that * and / have a higher precedence than + and -. But in the expression used for a leap
year check, note that year % 4 is computed before the subtraction because % has a higher precedence
than -.
Part 2 Refer to Part 2 of Program 6.3 (implicit_conversion.c), where we noted that the constant
1.0 must be placed at the beginning of the expression to prevent truncation in division. We have
achieved the same result here by placing the variable celsius before 9 / 5. The explanation is
simple: since / and * have the same precedence and L-R associativity, celsius * 9 is evaluated first
(but only after 9 has been converted to double).
192 Computer Fundamentals & C Programming

Tip: If you encounter an expression in the form b / c or b / c * a, where b and c are of type
int and a is of type float, you can easily prevent truncation of the decimal part. For b / c, either
multiply the expression by 1.0 at the beginning or use a cast for b or c. For b / c * a, rearrange
the operands to have a * b / c, in which case a cast is not necessary.

6.12 ASSIGNMENT OPERATORS


Let’s now resume our examination of the essential C operators by taking up the assignment and
increment/decrement operators. They are closely related to the arithmetic ones except that they
have the side effect of changing their operand. In this section, we discuss the operators related to
assignment (Table 6.3). These binary operators evaluate the expression on their right and assign
the result to the variable on the left.

6.12.1 The =: The Simple Assignment Operator


The = operator assigns the expression on its right to a variable on its left. The variable can be of any
type—including array elements and pointers—but we’ll begin with a simple assignment:
int max_tries = 5;
Is this assignment similar to the algebraic equation max_tries = 5? No, because in C, the left side
of the = must be a variable and not an expression like this one:
max_tries - 5 = 0; Makes perfect sense in algebra but not in C
You can have different types for the two operands, with or without adverse consequences. The type
of the expression is promoted or demoted to match the type of the variable:
float amount = 100; 100 converted to float; no problem
int balance = 100.5; 100.5 converted to int; truncation occurs
The = has a very low precedence and R-L associativity (Table 6.2). Thus, low precedence allows an
expression containing virtually any operator to be evaluated before assignment. You should now
understand the significance of the following statement that has been used a number of times before:
count = count + 1; Makes perfect sense in C but not in algebra
The + has a higher precedence than =, so addition is performed first with the current value of count.
The evaluated value is then assigned to the variable count on the left of the =. In the process, count
is incremented by one.

6.12.2 The Other Assignment Operators


The other operators of this family use the = in combination with each of the arithmetic operators.
There are thus five of them: +=, -=, *=, /= and %= (Table 6.3). Each of these two-character
operators performs a simple arithmetic operation on a variable on its left using an expression
on its right, and then stores the result in the same variable. This is how we use the += operator to add
5 to the existing value of count:
count += 5; Equivalent to count = count + 5
Operators and Expressions 193

This statement represents two separate tasks performed with a single operator. The += evaluates the
expression count + 5 before assigning the result to count. The evaluation is considered to be the
“main” task and the assignment is interpreted as a side effect. The other two-character assignment
operators (like -=) and the operators, ++ and --, also have side effects. Like the =, all of these
operators have low precedence and R-L associativity.
TABLE 6.3 The Assignment Operators
Operator Example Equivalent to
= count = 5; -
+= count += 3; count = count + 3;
-= count -= 3; count = count - 3;
*= count *= 4; count = count * 4;
/= count /= 4; count = count / 4;
%= count %= 5; count = count % 5;

6.13 ++ and --: INCREMENT AND DECREMENT OPERATORS


A programmer frequently needs to increment or decrement a variable by one—especially inside
a loop. C provides shortcuts for performing these two operations: the unary ++ and -- operators.
Each operator is offered in two varieties which are shown below for the ++ operator:
count++; Both expressions are identical to ...
++count; ... count += 1; and count = count + 1;
In either case, count is incremented by one. The ++ suffix used in the first example is known as
a postfix operator. The second example shows the ++ used as a prefix operator. We have identical
versions for the -- operator also. Both statements below decrement count by one:
count--; -- is a postfix operator
--count; -- is a prefix operator
The value of count changes every time these expressions are invoked. Even though there is
a difference between the prefix and postfix versions, they can be used interchangeably when used in
a standalone mode as above. They have L-R associativity but they don’t have the same precedence.
The postfix operators have a higher precedence than the prefix ones (Table 6.2).
6.13.1 Side Effect of ++ and --
Both ++ and -- have a side effect which shows the essential difference between the prefix and postfix
versions. For instance, in count++, the variable is evaluated before reassignment to the same variable.
For ++count, evaluation takes place after the assignment. Consider these examples:
count = 1;
printf(“count = %d\n”, count++); Prints 1 before incrementing count to 2
count = 1;
printf(“count = %d\n”, ++count); Increments count to 2 before printing 2
After execution is complete, count in each case is set to 2 even though printf displays different
values. The difference lies in the timing of the side effect that causes count to change its value.
This timing is crucial when using a while loop in the following ways:
194 Computer Fundamentals & C Programming

count = 10;
while (count-- > 0) Loop executes 10 times
count = 10;
while (--count > 0) Loop executes 9 times
In Chapter 8, you’ll acquire the skill to handle this difference of 1 that arises between the prefix
and postfix operations.

6.13.2 When Things Can Go Wrong


You must not prefix or postfix a variable multiple times in function arguments or in the same
expression. Enthusiastic programmers sometimes go overboard and use printf like this:
i = 11;
printf(“The first 3 integers exceeding 10 are %d, %d, %d\n”, i, i++, i++);
While one would expect the numbers 11, 12 and 13 to be printed, the results obtained on two
systems were found to be different. There seems to be no definite consensus on the sequence of
handling the printf arguments:
The first three integers exceeding 10 are 13, 12, 11 GCC
The first three integers exceeding 10 are 13, 13, 13 Visual Studio
A similar observation can be made on using ++ or -- multiple times with the same variable in an
expression. The following statements are meant to calculate the sum of the squares of the first
three integers:
int i = 3, sum;
sum = (i * i) + (--i * i) + (--i * i);
printf(“%d\n”, sum); Should it print 9 + 4 + 1 = 14?
We used parentheses for clarity; the precedence of the operators used in the expression don’t require
their use. On the author’s Linux system, sum evaluated to 5 when 14 was expected which means
that the evaluation did not start from the left.
Caution: The rules of C don’t specify the order of evaluation when ++ and -- are used multiple
times on the same variable in function arguments or an expression. Because the order of evaluation
is implementation-dependent, you must not use these operators in these two situations.

6.14 computation2.c: USING THE ASSIGNMENT AND ++/-- OPERATORS


Program 6.6 exploits the abbreviative features of the special arithmetic operators to offer
two important utilities. Using a while loop, the program computes (i) the sum of the first
100 integers, (ii) the result of an integer raised to a certain power. But before doing that, this three-
part program demonstrates the way the assignment operators are used.
Part 1 This part uses the five assignment operators on the variable a. Note the automatic generation
of the serial number for every line printed. Every time sl++ is printed, the side effect increments
the value of slno.
Operators and Expressions 195

/* computation2.c: Uses the assignment, ++ and -- operators to compute


(i) sum of first 100 integers (ii) power of a number. */
#include <stdio.h>
#define LAST_INT 100
int main(void)
{
int a = 3, slno = 1; /* slno prints the serial no */
int inta = 0, total1 = 0;
int intb = 0, base, power, total2 = 1;
/* 1. The assignment operators */
printf(“%d. Initial value of a = %d\n”, slno++, a);
a += 5; /* a is now 8, which ... */
printf(“%d. a += 5 = %d\n”, slno++, a); /* ... is confirmed here */
printf(“%d. a /= 2 = %d\n”, slno++, a /= 2);
printf(“%d. a *= 6 = %d\n”, slno++, a *= 6);
printf(“%d. a -= 3 = %d\n”, slno++, a -= 3);
printf(“%d. a %%= 6 = %d\n”, slno++, a %= 6);
/* 2. Sum of first 100 integers */
while (inta < LAST_INT + 1) {
total1 += inta; /* Can also use total1 += inta++ ... */
inta++; /* ... and eliminate this line */
}
printf(“%d. Total of first %d integers = %d\n”, slno++, LAST_INT, total1);
/* 3. Power of a number */
printf(“%d. Enter two numbers: “, slno++);
scanf(“%d%d”, &base, &power);
while (++intb < power + 1) /* No { and } required because ... */
total2 *= base; /* ... a single statement is executed */
printf(“%d. %d to the power %d = %d\n”, slno++, base, power, total2);
return 0;
}

PROGRAM 6.6: computation2.c


1. Initial value of a = 3
2. a += 5 = 8
3. a /= 2 = 4
4. a *= 6 = 24
5. a -= 3 = 21
6. a %= 6 = 3
7. Total of first 100 integers = 5050
8. Enter two numbers: 2 8
9. 2 to the power 8 = 256

PROGRAM OUTPUT: computation2.c


196 Computer Fundamentals & C Programming

Part 2 Unlike the technique used in Program 6.2, this program uses a while loop to calculate
the sum of the first 100 integers. The loop repeats as long as inta is less than LAST_INT + 1.
To know the sum of the first 200 integers, simply change the #define directive.
Part 3 This part calculates the power of a number (basepower), where base and power are integers
taken from user input. Because the *= repeatedly multiplies base by itself, total2 progressively
acquires the following values: 1, base, base * base, base * base * base, and so on. Note that this
while loop increments intb and checks its value in the same expression. Amazing power of C!

6.15 RELATIONAL OPERATORS AND EXPRESSIONS


Two of the tenets of structured programming—decision making and repetition—are implemented
using relational expressions. A relational expression comprises two expressions connected by
a relational operator. This expression can have only boolean values (true or false) and takes the
following general form:
exp1 relop exp2 relop signifies a relational operator
Here, exp1 or exp2 can be any expression. relop can be any of the relational operators shown in
Table 6.4 (like >, <, ==, etc.). For instance, x > y is a relational expression and has the value
1 or 0 depending on whether x is greater than y (1) or not (0). Consider this code snippet:
int x = 5, y = 7;
printf(“%d, %d\n”, x > y, x < y); Prints 0, 1
These two possible values are determined by the relation x has with y; hence the term “relational.”
The value 0 or 1 is of type int.
Takeaway: Unlike arithmetic expressions which are evaluated arithmetically, relational
expressions are evaluated logically. An expression here can take on only true (one) or false
(zero) values.

TABLE 6.4 The Relational Operators


Operator Expression Has the Value 1 if
< x < y x is less than y, 0 otherwise.
<= x <= y x is less than or equal to y, 0 otherwise.
> x > y x is greater than y, 0 otherwise.
>= x >= y x is greater than or equal to y, 0 otherwise.
== x == y x is equal to y, 0 otherwise.
!= x != y x is not equal to y, 0 otherwise.

6.15.1 Using the Operators


Relational expressions are used as control expressions with all decision making and loop constructs
(Chapters 7 and 8) to determine control flow. All control expressions must be enclosed within
parentheses as shown in the following examples:
Operators and Expressions 197

Construct Value of Control Expression is 1 if


if (your_number < 11) your_number is less than 11
while (count <= your_number) count is less than or equal to your_number
while (response == ‘y’) response is equal to ‘y’
if (index != 0) index is not equal to zero
if (index = 0) Wrong! Sets index to 0
The third example shows the use of the == operator (in a while loop) to test equality. Beginners often
make the mistake of using = (the assignment operator) instead of ==. For instance, the following
statement is used wrongly:
if (index = 0) No test here, changes value of index
The expression index = 0 is not a relational one but it still evaluates to 0 because of the assignment
it makes by using a wrong operator. The compiler may flag a warning but not an error because it
has no reason to think you have made a mistake even though you have.
Caution: Don’t use the = operator to check for the equality of two expressions because you’ll be
making an assignment instead. The == is the equality operator used in C.

Note: Even though we tend to think of 1 and 0 as true and false values, respectively, the term true
actually represents any non-zero value. Thus, the minimalist expression 5 is always true.

6.15.2 Precedence and Associativity


The relational operators have a lower precedence than the five arithmetic operators, but they rank
higher than the assignment operators. They also have L-R associativity. You’ll often find the
= combined with the relational operators, == and !=, in this manner:
(x = y) == z Without (), y == z would be evaluated first
(x = y) != z Without (), y != z would be evaluated first
Each expression is evaluated after making an assignment followed by a relational test. Because
= has a lower precedence than either == or !=, use of the parentheses ensures that y is assigned to x
before x is compared to z. The following example represents a practical application of this expression:
(c = getchar()) != EOF

This expression, which owes its origin to Kernighan and Ritchie, is commonly used inside a while
loop to repeatedly invoke the getchar function to read a character from a file or the terminal. Here,
the return value of getchar is assigned to c, which is then checked for non-equality with a symbolic
constant. This expression has the side effect of reading a character every time it is evaluated.
198 Computer Fundamentals & C Programming

6.16 THE LOGICAL OPERATORS


In real-life programming, decisions can’t always be made by making simple relational tests.
C handles complex decision making using the following three logical operators:
&& represents logical AND
|| represents logical OR
! represents logical NOT
The first two operators are binary while the third one is unary. These operators are meant to be
used with relational expressions even though any expression that evaluates to a value is a valid
operand for them.

6.16.1 The && & || Operators


The logical operators, && and !!, evaluate two expressions exp1 and exp2 individually as true or
false and then use these values to evaluate the combined or compound expression as true or false.
The syntax of both operators are shown below:
exp1 && exp2 exp1 and exp2 evaluate to true or false
exp1 || exp2 Ditto
&& and || are also known as the logical AND and logical OR operators, respectively. They conform
to the truth tables of the AND and OR gates, respectively (Figs. 3.1 and 3.2). Thus, the value of
the compound expression is true when
in the case of &&, both exp1 and exp2 individually evaluate to true.
in the case of ||, at least one of them is true.
Here’s an example of a compound expression that uses the && with two relational expressions as
its operands:
a > 0 && b > 0 A compound expression
If a has the value 0 and b has the value 5, the resultant expression evaluates to false because the first
expression is false. Now, let’s replace the && in the previous expression with ||:
a > 0 || b > 0
Assuming the same values of a and b, the compound expression is true because one of the expressions
is true (b > 0). In both cases, evaluation will begin from the left but it will stop mid-way if the
outcome of the entire expression has already been determined. For the &&, evaluation stops if a > 0
is false. For the ||, evaluation stops if a > 0 is true.
Both operators have a precedence lower than the relational operators but higher than the
assignment ones (Table 6.2). Thus, no parentheses are needed to enclose the relational expressions.
Between themselves, the && has a higher priority than the ||, but both have L-R associativity.
Chapters 7 and 8 fully exploit the power of the logical operators. It could still be helpful to know
how they are used in a program, say, by an if statement:
Operators and Expressions 199

if (a == 0 && b == 0) {
printf(“Both integers are zero\n”);
return 1; Intelligent use of return value
}
A compound expression can include more than one logical operator. The following expression
evaluates to true when all of the relational expressions also evaluate to true:
a <= 0 && b <= 0 && c <= 0
Compound expressions like the one above are useful only when we don’t want to know the precise
reason of failure; it’s either a, b or c, that’s all we care to know. Sometimes, you may need to make
separate decisions for each of three possible outcomes. In that case, you need to use a separate
if statement with each expression.

6.16.2 The ! Operator


The next logical operator we examine is the unary ! representing the NOT operation. This operator
negates the logical value of its operand. True becomes false and vice versa as is the case with the
NOT gate (Fig. 3.4). For instance, the two expressions on the following lines negate each other:
!(a > 0) a > 0
!(b == 1) b == 1
The ! has a higher precedence than the relational and logical operators (Table 6.2). Without the
parentheses, !a would represent the logical negation of a.
Table 6.5 presents some constructs that use the ! alongside their equivalent constructs that don’t
use it. The expression !(a > 0) signifies “a not greater than 0”, which is the same as (a <= 0).
Normally, an expression without the ! is easier to read than one with it, so you should avoid using
it to the extent you can.
TABLE 6.5 Use of Relational Operators with and without !
With Logical ! Without Logical !
if (!(a > 0)) if (a <= 0)
if (!(a >= 1)) if (a < 1)
while (!(a != 0)) while (a == 0)
if (a !> 0) Error; !> is an invalid operator
if (!a == 1) Not an error; but not what you meant

6.17 THE CONDITIONAL OPERATOR


C supports the only ternary operator—the conditional operator—which uses the symbols ? and :.
These two symbols are used with three expressions to implement a rudimentary form of decision
making. The operator uses the following formal syntax:
exp1 ? exp2 : exp3 Entire line is an expression

You might also like