Computer Programming Using C Language Ashish Seth Kirti Seth

(AMITY CENTER FOR e-LEARNING)
Computer Programming
Using
C Language
By:
Ashish Seth
Kirti Seth
1
(AMITY CENTER FOR e-LEARNING)
Preface
The question you always might think before learning C language is that why should I learn this
language? At present in the market, there are so many languages, using which you can meet most
of the industry needs. For the past many years, the solution for the most of the raw needs of the
industry has been C language? C is the heart of programming, one who learns this language can
command over any language very easily.
This book covers more than average discussions of C, It covers the features and functionality of
the language in depth. Starting chapter helps the reader to grasp the basic and structure of the
language, later chapter deals with the advance features of the language like pointers and file
handling. In order to asses your learning the quiz section is appended at the end of each chapter.
This assessment system is designed to evaluate the level of knowledge and skills as defined by
learning objectives
We have tried my best to make the book very useful for the students. Constructive, criticism, and
suggestions for the improvement of book are most welcome
It is pleasure to acknowledge my sincere gratitude to Prof. Dinesh Khurana (Amity International
Business School), Amity University Uttar Pradesh for giving us an opportunity and motivation to
write this book.
Thanks to our parents who gave us shower of love and believed our ability. we would like to
dedicate this work to our lovely daughters baby Verda and baby Vindhya. Finally, we thanks to
the Almighty who helped us in disguise in each and every step
Ashish Seth
Kirti Seth
2
(AMITY CENTER FOR e-LEARNING)
Index
Chapter No.
Title
Page No.
1.
Introduction to C
1
2.
Operators and Expressions
15
3.
Data Input/Output Functions
37
4.
Control Statements
48
5.
Functions
64
6.
Arrays
88
7.
Pointers
106
8.
Structures and Unions
126
9.
Data Files
138
10.
Key to End Chapter Quizzes
150
11.
Bibliography
152
3
(AMITY CENTER FOR e-LEARNING)
Chapter 1
Introduction to C
Contents:
1.1 Introduction
1.2 Data and Variables
1.2.1 Reserve words
1.2.2 Identifiers\
1.3 Data Types
1.3.1 Integer Number Variables
1.3.2 Decimal Number Variables
1.3.3 Character Variables
1.4 Form of C Program
1.1 Introduction
C is considered as High Level Languages. It uses English type statements that are converted to
machine statements which are executed by computers. C programs are simple text files
containing program statements. As such, it is created using a text editor. This is called the source
program. Once you have written the program, it must be compiled. The source program is
compiled by a special program called a compiler, whose task is to convert the statements in the
source code to either an intermediate format (called an object file), or an executable format
which can be run on the computer. There are a number of free compilers available on the
Internet. we briefly described the following stages in the development of a computer program:
(1) Define the problem.
(2) Analyze the problem.
(3) Develop an algorithm (a method) for solving the problem.
(4) Write the computer program which implements the algorithm.
(5) Test and debug (find the errors in) the program.
(6) Document the program. (Explain how the program works and how to use it).
(7) Maintain the program.
4
(AMITY CENTER FOR e-LEARNING)
1.2 Data and Variables
All computer programs, except the most trivial, are written to operate on data. For example:
• the data for an action game might be keys pressed or the position of the cursor when the mouse
is clicked;
• the data for a word processing program are the keys pressed while you are typing a letter;
• the data for an accounting program would include, among other things, expenses and income;
• the data for a program that teaches Spanish could be an English word that you type in response
to a question.
For a program to be run, it must be stored in the computer‘s memory. When data is supplied to a
program, that data is also stored in memory. Thus we think of memory as a place for holding
programs and data. One of the nice things about programming in a high-level language (like C or
Java) is that you don‘t have to worry about which memory locations are used to store your data.
But how do we refer to an item of data, given that there may be many data items in memory?
Think of memory as a set of boxes (or storage locations). Each box can hold one item of data, for
example, one number. We can give a name to a box, and we will be able to refer to that box by
the given name. In our example, we will need two boxes, one to hold the side of the square and
one to hold the area. We will call these boxes s and a, respectively.
If we wish, we can change the value in a box at any time; since the values can vary, s and a are
called variable names, or simply variables. Thus a variable is a name associated with a particular
memory location or, if you wish, it is a label for the memory location. We can speak of giving a
variable a value, or setting a variable to a specific value, such as 1, for instance. Important points
to remember are:
• a box can hold only one value at a time; if we put in a new value, the old one is lost;
• we must not assume that a box contains any value unless we specifically store a value in the
box. In particular, we must not assume that the box contains 0.
Variables are a common feature of computer programs. It is very difficult to imagine what
programming would be like without them. In everyday life, we often use variables. For example,
we speak of an ‗address‘. Here, ‗address‘ is a variable whose value depends on the person under
consideration. Other common variables are telephone number, name of school, subject, size of
population, type of car, television model, etc. (What are some possible values of these
variables?) A variable is a data name that may be used to store a data value. Variable may take
different values at different times during execution.
5
(AMITY CENTER FOR e-LEARNING)
Rules for declaring variable:
1. They must begin with a letter some systems permit underscore as the first character.
2. ANSI standard recognizes a length of 31 characters. However, the length should not be
normally more than eight characters, since only the first eight characters are treated as
significant by many compilers.
3. Uppercase & lowercase are significant .That is variable total is not the same as toTal or Total.
4. The variable name should not be a keyword.
5. White space is not allowed.
C's Character Set
C does not use, nor requires the use of, every character found on a modern computer keyboard.
The only characters required by the C Programming Language are as follows:
A-Z
a -z
0-9
space . , : ; ' $ "
# % & ! _ {} [] () < > |
+-/*=
1.2.1 Reserve words
The C language uses a number of keywords such as int, char and while. A keyword has a
special meaning in the context of a C program and can be used for that purpose only. For
example, int can be used only in those places where we need to specify that the type of some
item is integer. All keywords are written in lowercase letters only. Thus int is a keyword but Int
and INT are not. Keywords are reserved, that is, you cannot use them as your identifiers. As
such, they are usually called reserved words. All keywords have fixed meanings and these
meanings can not be changed. Keywords sever as basic building blocks for program statement.
There are 32 keywords, Example –break, case, char, do, if, else, int, float, for, goto, long,
void,while
1.2.2 Identifiers
The C programmer needs to make up names for things such as variables, function names and
symbolic constants (see next section). A name that he makes up is called a user identifier. There
are a few simple rules to follow in naming an identifier:
6
(AMITY CENTER FOR e-LEARNING)
Name of a variable (or any other item you define in program) is called identifier
identifier must start with a letter or underscore symbol (_), the rest of the characters
should be letters, digits or underscores
the following are valid identifiers:
x x1 x_1 _abc sum Rate averagE
the following are not legal identifiers. Why?
13 3X %change data-1 my.identifier a(3)
C is case sensitive:
MyVar and myvar are different identifiers
What are good identifiers
Careful selection of identifiers makes your program clearer
Identifiers should be Short enough to be reasonable to type (single word is norm)
Standard abbreviations are fine (but only standard abbreviations)
Long enough to be understandable
use abbreviations and underscores to separate the words, never use capital letters for
variables
1.3 Data Types
Now we have to start looking into the details of the C language. How easy you find the rest of
this section will depend on whether you have ever programmed before - no matter what the
language was. There are a great many ideas common to programming in any language and C is
no exception to this rule. So if you haven't programmed before, you need to take the rest of this
section slowly and keep going over it until it makes sense. If, on the other hand, you have
programmed before you'll be wondering what all the fuss is about It's a lot like being able to ride
a bike! The first thing you need to know is that you can create variables to store values in. A
variable is just a named area of storage that can hold a single value (numeric or character). C is
very fussy about how you create variables and what you store in them. It demands that you
declare the name of each variable that you are going to use and its type, or class, before you
actually try to do anything with it.
In this section we are only going to be discussing local variables. These are variables that are
used within the current program unit (or function) in a later section we will looking at global
variables - variables that are available to all the program's functions.
7
(AMITY CENTER FOR e-LEARNING)
There are five basic data types associated with variables:
int - integer: a whole number.
float - floating point value: ie a number with a fractional part.
double - a double-precision floating point value.
char - a single character.
void - valueless special purpose type which we will examine closely in later sections.
Ranges of data types:
int -------- -32,768 to 32767
char ------ -128 to 127
float ------ 3.4e-38 to 3.4e+38
Size:
int ----- 2 byte=16bit
char --- 1 byte=8bit
float --- 4 byte=32bit
One of the confusing things about the C language is that the range of values and the amount of
storage that each of these types takes is not defined. This is because in each case the 'natural'
choice is made for each type of machine. You can call variables what you like, although it helps
if you give them sensible names that give you a hint of what they're being used for - names like
sum, total, average and so on. If you are translating a formula then use variable names that
reflect the elements used in the formula. For example, 2 r (that should read as "2 pi r" but that
depends upon how your browser has been set-up) would give local variables names of pi and r.
Remember, C programmers tend to prefer short names!
Note: all C's variables must begin with a letter or a "_" (underscore) character.
1.3.1 Integer Number Variables
The first type of variable we need to know about is of class type int - short for integer. An int
variable can store a value in the range -32768 to +32767. You can think of it as a largish positive
or negative whole number: no fractional part is allowed. To declare an int you use the
instruction:
int variable name;
For example:
int a;
declares that you want to create an int variable called a.
To assign a value to our integer variable we would use the following C statement:
a=10;
The C programming language uses the "=" character for assignment. A statement of the form
a=10; should be interpreted as take the numerical value 10 and store it in a memory location
8
(AMITY CENTER FOR e-LEARNING)
associated with the integer variable a. The "=" character should not be seen as an equality
otherwise writing statements of the form:
a=a+10;
will get mathematicians blowing fuses! This statement should be interpreted as take the current
value stored in a memory location associated with the integer variable a; add the numerical value
10 to it and then replace this value in the memory location associated with a.
1.3.2 Decimal Number Variables
As described above, an integer variable has no fractional part. Integer variables tend to be used
for counting, whereas real numbers are used in arithmetic. C uses one of two keywords to declare
a variable that is to be associated with a decimal number: float and double. They are each offer a
different level of precision as outlined below.
float
A float, or floating point, number has about seven digits of precision and a range of about 1.E-36
to 1.E+36. A float takes four bytes to store.
double
A double, or double precision, number has about 13 digits of precision and a range of about 1.E303 to 1.E+303. A double takes eight bytes to store.
For example:
float total;
double sum;
To assign a numerical value to our floating point and double precision variables we would use
the following C statement:
total=0.0;
sum=12.50;
1.3.3 Character Variables
C only has a concept of numbers and characters. It very often comes as a surprise to some
programmers who learnt a beginner's language such as BASIC that C has no understanding of
strings but a string is only an array of characters and C does have a concept of arrays which we
shall be meeting later in this course. To declare a variable of type character we use the keyword
char. - A single character stored in one byte.For example:
char c;
9
(AMITY CENTER FOR e-LEARNING)
To assign, or store, a character value in a char data type is easy - a character variable is just a
symbol enclosed by single quotes. For example, if c is a char variable you can store the letter A
in it using the following C statement:
c='A'
Notice that you can only store a single character in a char variable. Later we will be discussing
using character strings, which has a very real potential for confusion because a string constant is
written between double quotes. But for the moment remember that a char variable is 'A' and not
"A".
Something To Declare
Before you can use a variable you have to declare it. As we have seen above, to do this you state
its type and then give its name. For example, int i; declares an integer variable. You can declare
any number of variables of the same type with a single statement. For example:
int a, b, c;
declares three integers: a, b and c. You have to declare all the variables that you want to use at
the start of the program. Later you will discover that exactly where you declare a variable makes
a difference, but for now you should put variable declarations after the opening curly bracket of
the main program.
Here is an example program that includes some of the concepts outlined above.
/*
Program#int.c
Another simple program
using int and printf
*/
#include <stdio.h>
main()
{
int a,b,average;
a=10;
b=6;
average = ( a+b ) / 2 ;
printf("Here ");
printf("is ");
printf("the ");
printf("answer... ");
printf("\n");
10
(AMITY CENTER FOR e-LEARNING)
printf("%d.",average);
}
More On Initialising Variables
You can assign an initial value to a variable when you declare it. For example:
int i=1;
sets the int variable to one as soon as it's created. This is just the same as:
int i;
i=l;
but the compiler may be able to speed up the operation if you initialise the variable as part of its
declaration. Don't assume that an uninitialised variable has a sensible value stored in it. Some C
compilers store 0 in newly created numeric variables but nothing in the C language compels
them to do so.
The form of a C Program
All C programs will consist of at least one function, but it is usual (when your experience grows)
to write a C program that comprises several functions. The only function that has to be present is
the function called main. For more advanced programs the main function will act as a controlling
function calling other functions in their turn to do the dirty work! The main function is the first
function that is called when your program executes.
C makes use of only 32 keywords which combine with the formal syntax to the form the C
programming language. Note that all keywords are written in lower case - C, like UNIX, uses
upper and lowercase text to mean different things. If you are not sure what to use then always
use lowercase text in writing your C programs. A keyword may not be used for any other
purposes. For example, you cannot have a variable called auto.
The layout of C Programs
The general form of a C program is as follows (don't worry about what everything means at the
moment - things will be explained later):
pre-processor directives
global declarations
main()
{
local variables to function main ;
statements associated with function main ;
}
f1()
11
(AMITY CENTER FOR e-LEARNING)
{
local variables to function 1 ;
statements associated with function 1 ;
}
f2()
{
local variables to function f2 ;
statements associated with function 2 ;
}
.
.
.
etc
Note the use of the bracket set () and {}. () are used in conjunction with function names whereas
{} are used as to delimit the C statements that are associated with that function. Also note the
semicolon - yes it is there, but you might have missed it! a semicolon (;) is used to terminate C
statements. C is a free format language and long statements can be continued, without truncation,
onto the next line. The semicolon informs the C compiler that the end of the statement has been
reached. Free format also means that you can add as many spaces as you like to improve the look
of your programs.
A very common mistake made by everyone, who is new to the C programming language, is to
miss off the semicolon. The C compiler will concatenate the various lines of the program
together and then tries to understand them - which it will not be able to do. The error message
produced by the compiler will relate to a line of you program which could be some distance from
the initial mistake.
12
(AMITY CENTER FOR e-LEARNING)
Chapter 2
Operators and Expressions
Contents:
2.1 Introduction
2.2 Precedence of C operators
2.3 Arithmetic operator
2.4 Relational and Logical Operators
2.5 Increment and Decrement operator
2.6 Assignment Operators and Expressions
2.6.1 Conditional Expressions
2.7 Bitwise operators
2.8 Miscellaneous Features
2.8.1
Casting
2.8.2
C Shorthand
2.8.3
Multiple Assignments
2.1 Introduction
Operators form expressions by joining individual constants, variables, and array. C includes a
large number of operators which fall into different categories. In this lesson we will see how
arithmetic operators, unary operators, relational and logical operators, assignment operators and
the conditional operators are used to form expressions. The data items on which operators act
upon are called operands. Some operators require two operands while others require only one
operand. Most operators allow the individual operands to be expressions. A few operators permit
only single variable as operand. Variables and constants can be used in conjunction with C
operators to create more complex expressions. Table 2-1 presents the set of C operators.
Table 2-1 C Operators
Operator
Example
Description/Meaning
()
f()
Function call
[]
a[10]
Array reference
13
(AMITY CENTER FOR e-LEARNING)
->
s->a
Structure and union member selection
.
s.a
Structure and union member selection
+ [unary]
+a
Value of a
- [unary]
-a
Negative of a
* [unary]
*a
Reference to object at address a
& [unary]
&a
Address of a
~
~a
One's complement of a
++ [prefix]
++a
The value of a after increment
++ [postfix] a++
The value of a before increment
- - [prefix]
The value of a after decrement
-a
- - [postfix] a-
The value of a before decrement
sizeof
sizeof (t1)
Size in bytes of object with type t1
sizeof
sizeof e
Size in bytes of object having the type of expression e
a plus b
+ [binary]
- [binary]
* [binary]
/%
a
a
a
a
a
+
*
/
%
b
b
b
b
b
a minus b
a times b
a divided by b
Remainder of a/b
>>
<<
a >> b
a << b
a, right-shifted b bits
a, left-shifted b bits
1 if a < b; 0 otherwise
<
>
<=
>=
==
!=
a
a
a
a
a
a
< b
> b
<= b
>= b
== b
!= b
1 if a > b; 0 otherwise
1 if a <= b; 0 otherwise
1 if a >= b; 0 otherwise
1 if a equal to b; 0 otherwise
1 if a not equal to b; 0 otherwise
14
(AMITY CENTER FOR e-LEARNING)
& [binary]
|
^
a & b
a | b
a ^ b
&&
||
!
a && b
a || b
!a
Bitwise AND of a and b
Bitwise OR of a and b
Bitwise XOR (exclusive OR) of a and b
Logical AND of a and b (yields 0 or 1)
Logical OR of a and b (yields 0 or 1)
Logical NOT of a (yields 0 or 1)
Expression e1 if a is nonzero;
?:
a ? e1 : e2
Expression e2 if a is zero
a, after b is assigned to it
a plus b (assigned to a)
=
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
,
a minus b (assigned to a)
a = b
a += b
a -= b
a *= b
a /= b
a %= b
a >>= b
a <<= b
a &= b
a |= b
a ^= b
e1,e2
a times b (assigned to a)
a divided by b (assigned to a)
Remainder of a/b (assigned to a)
a, right-shifted b bits (assigned to a)
a, left-shifted b bits (assigned to a)
a AND b (assigned to a)
a OR b (assigned to a)
a XOR b (assigned to a)
e2 (e1 evaluated first)
The C operators fall into the following categories:
Postfix operators, which follow a single operand.
Unary prefix operators, which precede a single operand.
Binary operators, which take two operands and perform a variety of arithmetic and
logical operations.
The conditional operator (a ternary operator), which takes three operands and evaluates
either the second or third expression, depending on the evaluation of the first expression.
Assignment operators, which assign a value to a variable.
The comma operator, which guarantees left-to-right evaluation of comma-separated
expressions.
15
(AMITY CENTER FOR e-LEARNING)
2.2 Precedence of C Operators
Operator precedence determines the grouping of terms in an expression. This affects how an
expression is evaluated. Certain operators have higher precedence than others; for example, the
multiplication operator has higher precedence than the addition operator:
x = 7 + 3 * 2;
/* x is assigned 13, not 20
*/
The previous statement is equivalent to the following:
x = 7 + (3 * 2);
Using parenthesis in an expression alters the default precedence. For example:
x = (7 + 3) * 2;
/*
(7 + 3) is evaluated first
*/
In an unparenthesized expression, operators of higher precedence are evaluated before those of
lower precedence. Consider the following expression:
A+B*C
The identifiers B and C are multiplied first because the multiplication operator (*) has higher
precedence than the addition operator (+).
Category
Operator
Associativity
Postfix
() [] -> . ++ - -
Left to right
Unary
+ - ! ~ ++ - - (type) * & sizeof
Right to left
Multiplicative * / %
Left to right
Additive
+-
Left to right
Shift
<< >>
Left to right
Relational
< <= > >=
Left to right
Equality
== !=
Left to right
Bitwise AND
&
Left to right
Bitwise XOR
^
Left to right
Bitwise OR
|
Left to right
Logical AND
&&
Left to right
Logical OR
||
Left to right
Conditional
?:
Right to left
Assignment
= += -= *= /= %= >>= <<= &= ^= |= Right to left
Comma
,
Left to right
16
(AMITY CENTER FOR e-LEARNING)
Table 2-2 Precedence of C Operators
Associativity relates to precedence, and resolves any ambiguity over the grouping of operators
with the same precedence. In the following statement, the rules of C specify that a * b is
evaluated first:
y = a * b / c;
In a more complicated example, associativity rules specify that b ? c : d is evaluated first in
the following example:
a ? b ? c : d : e;
The associativity of the conditional operator is right-to-left on the line. The assignment operator
also associates right-to-left; for example:
int x = 0 , y = 5, z = 3;
x = y = z;
/*
x has the value 3, not 5
*/
Other operators associate left-to-right; for example, the binary addition, subtraction,
multiplication, and division operators all have left-to-right associativity.
Associativity applies to each row of operators in Table 2-2 and is right-to-left for some rows and
left-to-right for others. The kind of associativity determines the order in which operators from
the same row are evaluated in an unparenthesized expression. Consider the following expression:
A*B%C
This expression is evaluated as follows because the multiplicative operators (*, /, %) are
evaluated from left to right:
(A*B)%C
Parentheses can always be used to control precedence and associativity within an expression
2.3 Arithmetic Operators
The binary arithmetic operators are +, -, *, /, and the modulus operator %. Integer division
truncates any fractional part. The expression
x%y
produces the remainder when x is divided by y, and thus is zero when y divides x exactly. For
example, a year is a leap year if it is divisible by 4 but not by 100, except that years divisible by
400 are leap years. Therefore
17
(AMITY CENTER FOR e-LEARNING)
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
printf("%d is a leap year\n", year);
else
printf("%d is not a leap year\n", year);
The % operator cannot be applied to a float or double. The direction of truncation for / and the
sign of the result for % are machine-dependent for negative operands, as is the action taken on
overflow or underflow.
The binary + and - operators have the same precedence, which is lower than the precedence of *,
/ and %, which is in turn lower than unary + and -. Arithmetic operators associate left to right.
2.4 Relational and Logical Operators
The relational and logical operators are as follows:
Operator
Meaning
&gt
Greater than
&gt=
Greater than or equal to
<
&lt=
Less than
Less than or equal to
==
Equal
!=
Not equal
&amp&
logical AND
||
logical OR
!
logical NOT
The idea of true and false underlies the concepts of relational and logical operators. In C, true is
any value other than zero and false is zero. All relational and logical operations produce a result
of either 1 or 0.
18
(AMITY CENTER FOR e-LEARNING)
Although C does not contain an exclusive OR (XOR) operator, a function can easily be created
to perform that task.
Mostly used relational operators are
> >= < <=
They all have the same precedence. Just below them in precedence are the equality operators:
== !=
Relational operators have lower precedence than arithmetic operators, so an expression like i <
lim-1 is taken as i < (lim-1), as would be expected.
More interesting are the logical operators && and ||. Expressions connected by && or || are
evaluated left to right, and evaluation stops as soon as the truth or falsehood of the result is
known. Most C programs rely on these properties.
for (i=0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
Before reading a new character it is necessary to check that there is room to store it in the array s,
so the test i < lim-1 must be made first. Moreover, if this test fails, we must not go on and read
another character.
Similarly, it would be unfortunate if c were tested against EOF before getchar is called;
therefore the call and assignment must occur before the character in c is tested.
The precedence of && is higher than that of ||, and both are lower than relational and equality
operators, so expressions like
i < lim-1 && (c=getchar()) != '\n' && c != EOF
need no extra parentheses. But since the precedence of != is higher than assignment, parentheses
are needed in
(c=getchar()) != '\n'
to achieve the desired result of assignment to c and then comparison with '\n'.
By definition, the numeric value of a relational or logical expression is 1 if the relation is true,
and 0 if the relation is false.
19
(AMITY CENTER FOR e-LEARNING)
The unary negation operator ! converts a non-zero operand into 0, and a zero operand in 1. A
common use of ! is in constructions like
if (!valid)
rather than
if (valid == 0)
It's hard to generalize about which form is better. Constructions like !valid read nicely (``if not
valid''), but more complicated ones can be hard to understand.
Exercise. Write a loop equivalent to the for loop above without using && or ||.
2.5 Increment and Decrement Operators
C provides two unusual operators for incrementing and decrementing variables. The increment
operator ++ adds 1 to its operand, while the decrement operator -- subtracts 1. We have
frequently used ++ to increment variables, The increment operator "++" increments the named
variable. For example, the statement "a++" is equivalent to "a= a+1" or "a+= 1". A statement that
uses an increment operator has a value. For example, the statement
a= 3;
printf("a=%d a+1=%d\n", a, ++a);
will display the text "a=3 a+1=4."
If the increment operator comes after the named variable, then the value of the statement is
calculated after the increment occurs. So the statement
example 1,
a= 3;
printf("a=%d a+1=%d\n", a, a++);
would display "a=3 a+1=3" but would finish with a set to 4.
The decrement operator "--" is used in the same fashion as the increment operator.
example 2,
if (c == '\n')
++nl;
20
(AMITY CENTER FOR e-LEARNING)
The unusual aspect is that ++ and -- may be used either as prefix operators (before the variable,
as in ++n), or postfix operators (after the variable: n++). In both cases, the effect is to increment
n. But the expression ++n increments n before its value is used, while n++ increments n after its
value has been used. This means that in a context where the value is being used, not just the
effect, ++n and n++ are different. If n is 5, then
x = n++;
sets x to 5, but
x = ++n;
sets x to 6. In both cases, n becomes 6. The increment and decrement operators can only be
applied to variables; an expression like (i+j)++ is illegal.
In a context where no value is wanted, just the incrementing effect, as in
if (c == '\n')
nl++;
prefix and postfix are the same. But there are situations where one or the other is specifically
called for. For instance, consider the function squeeze(s,c), which removes all occurrences of the
character c from the string s.
/* squeeze: delete all c from s */
void squeeze(char s[], int c)
{
int i, j;
for (i = j = 0; s[i] != '\0'; i++)
if (s[i] != c)
s[j++] = s[i];
s[j] = '\0';
}
Each time a non-c occurs, it is copied into the current j position, and only then is j incremented to
be ready for the next character. This is exactly equivalent to
if (s[i] != c) {
s[j] = s[i];
j++;
}
As another example, consider the standard function strcat(s,t), which concatenates the string t to
the end of string s. strcat assumes that there is enough space in s to hold the combination. As we
have written it, strcat returns no value; the standard library version returns a pointer to the
resulting string.
/* strcat: concatenate t to end of s; s must be big enough */
void strcat(char s[], char t[])
{
int i, j;
i = j = 0;
while (s[i] != '\0') /* find end of s */
i++;
while ((s[i++] = t[j++]) != '\0') /* copy t */
21
(AMITY CENTER FOR e-LEARNING)
;
}
As each member is copied from t to s, the postfix ++ is applied to both i and j to make sure that
they are in position for the next pass through the loop.
Exercise. Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that
matches any character in the string s2.
Exercise. Write the function any(s1,s2), which returns the first location in a string s1 where
any character from the string s2 occurs, or -1 if s1 contains no characters from s2. (The standard
library function strpbrk does the same job but returns a pointer to the location.)
2.6 Assignment Operators and Expressions
An expression such as
i=i+2
in which the variable on the left side is repeated immediately on the right, can be written in the
compressed form
i += 2
The operator += is called an assignment operator.
Most binary operators (operators like + that have a left and right operand) have a corresponding
assignment operator op=, where op is one of
+ - * / % << >> & ^ |
If expr1 and expr2 are expressions, then
expr1 op= expr2
is equivalent to
expr1 = (expr1) op (expr2)
except that expr1 is computed only once. Notice the parentheses around expr2:
x *= y + 1
means
x = x * (y + 1)
rather than
x=x*y+1
As an example, the function bitcount counts the number of 1-bits in its integer argument.
/* bitcount: count 1 bits in x */
int bitcount(unsigned x)
{
int b;
for (b = 0; x != 0; x >>= 1)
22
(AMITY CENTER FOR e-LEARNING)
if (x & 01)
b++;
return b;
}
Declaring the argument x to be an unsigned ensures that when it is right-shifted, vacated bits will
be filled with zeros, not sign bits, regardless of the machine the program is run on.
Quite apart from conciseness, assignment operators have the advantage that they correspond
better to the way people think. We say ``add 2 to i'' or ``increment i by 2'', not ``take i, add 2,
then put the result back in i''. Thus the expression i += 2 is preferable to i = i+2. In addition, for a
complicated expression like
yyval[yypv[p3+p4] + yypv[p1]] += 2
the assignment operator makes the code easier to understand, since the reader doesn't have to
check painstakingly that two long expressions are indeed the same, or to wonder why they're not.
And an assignment operator may even help a compiler to produce efficient code.
We have already seen that the assignment statement has a value and can occur in expressions; the
most common example is
while ((c = getchar()) != EOF)
...
The other assignment operators (+=, -=, etc.) can also occur in expressions, although this is less
frequent.
In all such expressions, the type of an assignment expression is the type of its left operand, and
the value is the value after the assignment.
Exercise In a two's complement number system, x &= (x-1) deletes the rightmost 1-bit in x.
Explain why. Use this observation to write a faster version of bitcount.
2.6.1 Conditional Expressions
The statements
if (a > b)
z = a;
else
z = b;
23
(AMITY CENTER FOR e-LEARNING)
compute in z the maximum of a and b. The conditional expression, written with the ternary
operator ``?:'', provides an alternate way to write this and similar constructions. In the expression
expr1 ? expr2 : expr3
the expression expr1 is evaluated first. If it is non-zero (true), then the expression expr2 is
evaluated, and that is the value of the conditional expression. Otherwise expr3 is evaluated, and
that is the value. Only one of expr2 and expr3 is evaluated. Thus to set z to the maximum of a and
b,
z = (a > b) ? a : b; /* z = max(a, b) */
It should be noted that the conditional expression is indeed an expression, and it can be used
wherever any other expression can be. If expr2 and expr3 are of different types, the type of the
result is determined by the conversion rules discussed earlier in this chapter. For example, if f is
a float and n an int, then the expression
(n > 0) ? f : n
is of type float regardless of whether n is positive.
Parentheses are not necessary around the first expression of a conditional expression, since the
precedence of ?: is very low, just above assignment. They are advisable anyway, however, since
they make the condition part of the expression easier to see.
The conditional expression often leads to succinct code. For example, this loop prints n elements
of an array, 10 per line, with each column separated by one blank, and with each line (including
the last) terminated by a newline.
for (i = 0; i < n; i++)
printf("%6d%c", a[i], (i%10==9 || i==n-1) ? '\n' : ' ');
A newline is printed after every tenth element, and after the n-th. All other elements are followed
by one blank. This might look tricky, but it's more compact than the equivalent if-else. Another
good example is
printf("You have %d items%s.\n", n, n==1 ? "" : "s");
Exercise. Rewrite the function lower, which converts upper case letters to lower case, with a
conditional expression instead of if-else.
2.7 Bitwise Operators
Since C was designed to take the place of assembly language for most programming tasks, it
needed to be able to support assembler-like operations. Bitwise operations refer to testing,
setting or shifting the actual bits in a byte or word (byte and word corresponding to the types
char and int and their variants).
C provides six operators for bit manipulation; these may only be applied to integral operands,
that is, char, short, int, and long, whether signed or unsigned.
& bitwise AND
24
(AMITY CENTER FOR e-LEARNING)
|
^
<<
>>
~
bitwise inclusive OR
bitwise exclusive OR
left shift
right shift
one's complement (unary)
The bitwise AND operator & is often used to mask off some set of bits, for example
n = n & 0177;
sets to zero all but the low-order 7 bits of n.
The bitwise OR operator | is used to turn bits on:
x = x | SET_ON;
sets to one in x the bits that are set to one in SET_ON.
The bitwise exclusive OR operator ^ sets a one in each bit position where its operands have
different bits, and zero where they are the same.
One must distinguish the bitwise operators & and | from the logical operators && and ||, which
imply left-to-right evaluation of a truth value. For example, if x is 1 and y is 2, then x & y is zero
while x && y is one.
The shift operators << and >> perform left and right shifts of their left operand by the number of
bit positions given by the right operand, which must be non-negative. Thus x << 2 shifts the
value of x by two positions, filling vacated bits with zero; this is equivalent to multiplication by
4. Right shifting an unsigned quantity always fits the vacated bits with zero. Right shifting a
signed quantity will fill with bit signs (``arithmetic shift'') on some machines and with 0-bits
(``logical shift'') on others.
The unary operator ~ yields the one's complement of an integer; that is, it converts each 1-bit into
a 0-bit and vice versa. For example
x = x & ~077
sets the last six bits of x to zero. Note that x & ~077 is independent of word length, and is thus
preferable to, for example, x & 0177700, which assumes that x is a 16-bit quantity. The portable
form involves no extra cost, since ~077 is a constant expression that can be evaluated at compile
time.
25
(AMITY CENTER FOR e-LEARNING)
As an illustration of some of the bit operators, consider the function getbits(x,p,n) that returns the
(right adjusted) n-bit field of x that begins at position p. We assume that bit position 0 is at the
right end and that n and p are sensible positive values. For example, getbits(x,4,3) returns the
three bits in positions 4, 3 and 2, right-adjusted.
/* getbits: get n bits from position p */
unsigned getbits(unsigned x, int p, int n)
{
return (x >> (p+1-n)) & ~(~0 << n);
}
The expression x >> (p+1-n) moves the desired field to the right end of the word. ~0 is all 1-bits;
shifting it left n positions with ~0<<n places zeros in the rightmost n bits; complementing that
with ~ makes a mask with ones in the rightmost n bits.
Exercise. Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p
set to the rightmost n bits of y, leaving the other bits unchanged.
Exercise. Write a function invert(x,p,n) that returns x with the n bits that begin at position p
inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged.
Exercise. Write a function rightrot(x,n) that returns the value of the integer x rotated to the right
by n positions.
Clearing a Bit - Bitwise AND (&amp)
Bitwise ANDing is frequently used for &quotmasking"operations, i.e., the operator may be used
to set specific bits to zero. Any bit that is 0 in either operand causes the corresponding bit to be
set to 0. For example, the following resets a parity bit to 0:
return (ch & 127);
Note that the number 127 has bit 8 set to 0. Here is what happens:
parity bit set
parity bit unset
1 0 0 1 0 1 0 1
0 0 0 1 0 1 0 1
ch
0 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1
127
0 0 0 1 0 1 0 1
0 0 0 1 0 1 0 1
result
26
(AMITY CENTER FOR e-LEARNING)
Setting a Bit - Bitwise OR (|)
Similarly, the bitwise OR may be used to set a bit. Any bit which is set to 1 in either operand
causes the outcome to be set to 1. For example, the following is 128 | 3:
0 0 0 0 0 0 1 1
3 in binary
1 0 0 0 0 0 0 0
128 in binary
1 0 0 0 0 0 1 1
result
Exclusive OR (^)
Exclusive OR (XOR) sets a bit only if the bits being compared are different. One interesting
property of XOR is that any value XOR'd with itself produces 0. This is useful in assembly
programming.
Swapping Values. An interesting application of XOR is that it can be used to exchange two
values without the need for an extra memory location, e.g.:
a ^= b;
b ^= a;
a ^= b;
Shift Operators (&lt&lt and &gt&gt)
Shift operators move all bits left or right as specified. As bits are shifted off one end, zeroes are
brought in at the other. A shift is not a rotate; the bits shifted off one end are lost.
Left shifting has the effect of multiplying the shifted value by two. For right shifts, what is
shifted in on the left depends on the sign of the value being shifted and also on how the operation
is implemented on the particular machine. If the sign is 0 (positive), then zeroes will be shifted
in. If the sign is 1 (negative), on some machines 1s will be shifted in and on others, 0s. The
former is known as an arithmetic right shift; the latter is known as a logical right shift.
C does not guarantee a defined result if a value is shifted by an amount greater or less than the
number of bits in the data item.
27
(AMITY CENTER FOR e-LEARNING)
One's Complement Operator (~)
The One's Complement operator reverses the state of each bit in the specified variable. Note that
two complements in a row return the byte to its original value.
The ? Operator
The ternary operator ? replaces certain statements of the if-then-else form. In the following:
y = x >10 ? 100 : 200
the first expression is evaluated. If it evaluates to true, y is made equal to 100. If it evaluates to
false, y is made equal to 200.
The &amp and * Operators
Preamble. A pointer is the memory address of a variable. A pointer variable is a variable
specifically declared to hold a pointer. Pointers:
provide a fast means of referencing array elements,
allow C functions to modify their calling parameters, and
support linked lists and other dynamic data structures.
A pointer is of sufficient size to hold an address as defined by the architecture of the computer.
Pointer Operators. The following operators are used to manipulate pointers:
The & Operator
The & operator returns the memory address of its operand. For example:
m = &count;
places the address of count into m. & can be thought of as "address of".
28
(AMITY CENTER FOR e-LEARNING)
The * Operator
The * operator is the complement of &amp. It returns the value of the variable at the address
which follows it. For example, in the following extension of the preceding code fragment:
q = *count;
the value of count is placed in q. * can be thought of as "address".
The Compile-Time Operator sizeof
sizeof is a unary compile time operator which returns the length, in bytes, of the variable or
parenthesized type specifier that precedes it. Usage is illustrated in the following:
printf(&quot%f&quot, sizeof f);
printf(&quot%d&quot, sizeof(int));
The Comma Operator
The comma operator may be used to string together several expressions. For example, in:
x - (y = 3, y + 1);
the left side of the comma operator is evaluated as void, which means that y is first assigned the
value 3 and then x is assigned the value 4.
The . and -> Operators
The . and -> operators reference individual elements in structures and unions. The . operator is
used when working with the actual structure or union, e.g.:
employee.wage = 123.23;
The -&gt is used when working with a pointer to the structure or union, e.g.:
emp_ptr->wage = 123.23;
29
(AMITY CENTER FOR e-LEARNING)
2.8 Miscellaneous Features
2.8.1 Casting
An expression can be forced to be of a particular type by using a cast. Casts are often considered
to be operators. As an operator, casts are unary, and have the same precedence as other unary
operators. An example of a cast which ensures that an expression evaluates to type float is as
follows:
x = (float) x / 2;
In the following example, a fractional number is truncated to a whole number:
double x = 3.141592654;
double y;
y = (short) x; // resulting value of y is 3.0
In the following example, casting is used to round to the nearest integer:
double x, y;
x = 4.67;
y = (short) (x + 0.5); // resulting value of y is 5.0
x = 4.45;
y = (short) (x +.05); // resulting value of y is 4.0
2.8.2 C Shorthand
C has a special shorthand which simplifies the coding of certain assignments. For example:
x = x + 10; can be written as x += 10;
x = x * 10; can be written as x *= 10;
Some further examples using the bitwise operators are as follows:
30
(AMITY CENTER FOR e-LEARNING)
Operator
Usage
Result in a After Operation
&gt&gt=
a &gt&gt= b a shifted right by b bits
&lt&lt=<
a &lt&lt= b
a shifted left by b bits
&amp=
a &amp= b
a ANDed with b
^=
a ^= b
a exclusive ORed with b
|=
a |= b
a ORed with b
2.8.3 Multiple Assignments
The assignment of a value to many variables is possible using multiple assignment, eg:
x = y = z = 0;
Chapter 3
Data Input / Output Functions
Contents:
3.1 Introduction
3.2 Standard input output
3.3 Character Input / Output
3.3.1 getchar
3.3.2 putchar
3.4 Formatted input /output
3.4.1 printf
3.4.2 scanf
3.5 whole lines of input and output
31
(AMITY CENTER FOR e-LEARNING)
3.5.1 gets
3.5.2 puts
3.1 Introduction
One of the reasons that has prevented many programming languages from becoming widely used
for ‗real programming‘ is their poor support for I/O, a subject which has never seemed to excite
language designers. C has avoided this problem, oddly enough, by having no I/O at all! The C
language approach has always been to do I/O using library functions, which ensures that system
designers can provide tailored I/O instead of being forced to change the language itself.
As C has evolved a library package known as the ‗Standard I/O Library‘ or stdio, has evolved
with it and has proved to be both flexible and portable. This package has now become part of the
Standard.
The old stdio package relied heavily on the UNIX model of file access, in particular the
assumption that there is no distinction between unstructured binary files and files containing
readable text. Many operating systems do maintain a distinction between the two, and to ensure
that C programs can be written portably to run on both types of file model, the stdio package has
been modified. There are changes in this area which affect many existing programs, although
strenuous efforts were taken to limit the amount of damage.
3.2 The standard Input Output File.
To perform, input and output to files or the terminal, UNIX supplies a standard package. To use
them, this contains most of the functions which will be introduced in this section, along with
definitions of the data types required. Your program must include these definitions by adding the
line to use these facilities.
#include near start of the program file. If you do not do this, the compiler may complain about
undefined data types or functions.
32
(AMITY CENTER FOR e-LEARNING)
3.3 Character Input / Output
If you will not press the return key then they‘ll not start reading any input, and they‘ll not print
characters on the terminal until there is a whole line to be printed. This is the lowest level of
output and input. It provides very precise control, but is usually too fiddly to be useful also. Most
computers perform buffering of inputs and outputs. These include:
1. getchar
2. putchar
3.3.1 getchar
getchar always returns the next character of keyboard input as an int. The EOF (end of file) is
returned,if there is an error . It is usual to compare this value against EOF before using it. So
error conditions will not be handled correctly,if the return value is stored in a char, it will never
be equal to EOF.
The following program is used to count the number of characters read until an EOF is
encountered. EOF can be generated by typing Control - d.
#include
main()
{ int ch, i = 0;
while((ch = getchar()) != EOF)
i ++;
printf(‖%d\n‖, i);
}
3.3.2 putchar
The putchar() function writes ch to STDOUT. The code
putchar( ch );
33
(AMITY CENTER FOR e-LEARNING)
is the same as
putc( ch, STDOUT );
The return value of putchar() is the written character, or EOF if there is an error putchar puts its
character argument on the standard output (usually the screen). The following example program
converts any typed input into capital letters.It applies the function toupper from the character
conversion library ctype.h to each character in turn to do this.
#include /* For definition of toupper */
#include /* For definition of getchar, putchar, EOF */
main()
{ int ch;
while((ch = getchar()) != EOF)
putchar(toupper(ch));
}
3.4 Formatted Input / Output
They are closest to the facilities offered by Pascal or Fortran, and usually the easiest to use for
input and output. The versions offered under C are a little more detailed, offering precise control
of layout.
These includes following:
1. printf
2. scanf
3.4.1 printf
34
(AMITY CENTER FOR e-LEARNING)
This offers more structured output than the putchar. Its arguments are, in order; a control string,
which controls what get printed, followed by a list of values to be substituted for entries in the
control string.The printf() function prints output to STDOUT, according to format and other
arguments passed to printf(). The string format consists of two types of items - characters that
will be printed to the screen, and format commands that define how the other arguments to
printf() are displayed. Basically, you specify a format string that has text in it, as well as
"special"
characters
that
map
to
the
other
arguments
of
printf().
The %s means, "insert the first argument, a string, right here." The %d indicates that the second
argument (an integer) should be placed there. There are different %-codes for different variable
types, as well as options to limit the length of the variables and whatnot.
Code
Format
----
------
%c
character
%d
signed integers
%i
signed integers
%e
scientific notation, with a lowercase "e"
%E
scientific notation, with a uppercase "E"
%f
floating point
%g
use %e or %f, whichever is shorter
%G
use %E or %f, whichever is shorter
%o
octal
%s
a string of characters
%u
unsigned integer
%x
unsigned hexadecimal, with lowercase letters
35
(AMITY CENTER FOR e-LEARNING)
%X
unsigned hexadecimal, with uppercase letters
%p
a pointer
%n
the argument shall be a pointer to an integer into which is placed the number of
characters written so far
%%
a '%' sign
An integer placed between a % sign and the format command acts as a minimum field width
specifier, and pads the output with spaces or zeros to make it long enough. If you want to pad
with zeros, place a zero before the minimum field width specifier. You can use a precision
modifier, which has different meanings depending on the format code being used.
With %e, %E, and %f, the precision modifier lets you specify the number of decimal
places desired. For example,
%12.6f
will display a floating number at least 12 digits wide, with six decimal places.
With %g and %G, the precision modifier determines the maximum number of significant
digits displayed.
With %s, the precision modifer simply acts as a maximum field length, to complement
the minimum field length that precedes the period.
All of printf()'s output is right-justified, unless you place a minus sign right after the % sign. For
example,
%-12.4f
will display a floating point number with a minimum of 12 characters, 4 decimal places, and left
justified. You may modify the %d, %i, %o, %u, and %x type specifiers with the letter l and the
letter h to specify long and short data types (e.g. %hd means a short integer). The %e, %f, and
%g type specifiers can have the letter l before them to indicate that a double follows. The %g,
%f, and %e type specifiers can be preceded with the character '#' to ensure that the decimal point
will be present, even if there are no decimal digits. The use of the '#' character with the %x type
36
(AMITY CENTER FOR e-LEARNING)
specifier indicates that the hexidecimal number should be printed with the '0x' prefix. The use of
the '#' character with the %o type specifier indicates that the octal value should be displayed with
a 0 prefix.
You can also include constant escape sequences in the output string.The return value of printf()
is the number of characters printed, or a negative number if an error occurred.
Example:
char name[20] = "Bob";
int age = 21;
printf( "Hello %s, you are %d years old\n", name, age );
OUTPUT: Hello Bob, you are 21 years old
3.4.1 scanf
To grab things from input,scanf() is used. Beware though; scanf isn‘t greatest function that C has
to offer. Some people brush off the scanf as a broken function that shouldn‘t be used often. The
prototype for scanf is:
int scanf( const char *format, …);
To read of data from the keyboard scanf allows formatted. Like printf it has a control string,
followed by the list of items to be read. However scanf wants to know the address of items to be
read, since it is a function which will change that value. Therefore the names of variables are
preceded by the & sign. Character strings are an exception to this. Since a string is already a
character pointer, we give the names of the string variables unmodified by a leading &. Control
string entries which match values to be read are preceded by the percentage sign in a similar way
to their printf equivalent. Looks similar to printf, but doesn‘t completely behave like the printf
does. Take the example:
scanf(‖%d‖, x);
37
(AMITY CENTER FOR e-LEARNING)
The following is the example which shows the use of scanf:
int x, args;
for ( ; ; ) {
printf(‖Enter an integer bub: ―);
if (( args = scanf(‖%d‖, &x)) == 0) {
printf(‖Error: not an integer\n‖);
continue;
} else {
if (args == 1)
printf(‖Read in %d\n‖, x);
else
break;
}
}
3.5 whole lines of input and output
Where we are not too interested in the format of our data, or perhaps we cannot predict its format
in advance, we can read and write whole lines as character strings. This approach allows us to
read in a line of input, and then use various string handling functions to analyse it at our leisure
3.5.1 puts
The function puts() writes str to STDOUT. puts() returns non-negative on success, or EOF on
failure. Explanation: This function, puts, will output the string pointed to by astring. It will then
add a newline character. This is built in to the function. It returns a nonnegative integer if it was
successful. It returns EOF if there was an error
Syntax:
#include <stdio.h>
38
(AMITY CENTER FOR e-LEARNING)
int puts( char *str );
//Example outputs "Hello, World" with a newline
#include <cstdio>
using namespace std;
int main()
{
puts("Hello World");
}
3.5.2 gets
The gets() function reads characters from STDIN and loads them into str, until a newline or EOF
is reached. The newline character is translated into a null termination. The return value of gets()
is the read-in string, or NULL if there is an error.
Syntax:
#include <stdio.h>
Chapter 4
Control Statements
Contents:
4.1 Preliminaries
4.1.1
4.1.2
Branching
4.1.1.1
If-else
4.1.1.2
Switch-case
Looping
4.1.2.1
For loop
4.1.2.2
While – do while
4.1.2.3
Break - continue
39
(AMITY CENTER FOR e-LEARNING)
4.1
Preliminaries
C provides two styles of flow control:
Branching
Looping
These branching and looping in C can be achieved with the help of following keywords
If-Else
While
For
Break / Continue
Switch-Case
We will discuss each of these one by one to understand their syntax and their application in
programming language
4.1.1Branching
Branching is deciding what actions to take and looping is deciding how many times to take a
certain action.Branching is so called because the program chooses to follow one branch or
another
4.1.1.1 if statement
This is the simplest form of the branching statements.
It takes an expression in parenthesis and an statement or block of statements. if the expression is
true then the statement or block of statements gets executed otherwise these statements are
skipped. Programs need to make decisions. Should this statement be executed or that one? Are
these parameters correct and so on. This is done using the if statement. The syntax is pretty easy.
if ( conditional expression )
statement;
This can be extended with else.
40
(AMITY CENTER FOR e-LEARNING)
If-Else
The if else statement is used to make decisions. The syntax is:
if (expression)
statement-1
else
statement-2
expression is evaluated; if it is not equal to zero (e.g., logic true), then statement-1 is executed.
The else clause is optional. If the if part of the statement did not execute, and the else is present,
then statement-2 executes.
if ( conditional expression )
statement1;
else
statement2;
So if the expression is true, statement1 is executed, otherwise statement2 is executed.
NOTE: Expression will be assumed to be true if its evaulated values is non-zero.
if , if-else , if-elseif-else statements take the following form:
if (expression)
statement;
or
if (expression)
{
Block of statements;
}
or
if (expression)
{
Block of statements;
}
else
{
Block of statements;
}
41
(AMITY CENTER FOR e-LEARNING)
or
if (expression)
{
Block of statements;
}
else if(expression)
{
Block of statements;
}
else
{
Block of statements;
}
Here are some examples:
if ( a==3 )
b=a;
if ( b==0 )
{
b=1;
a += 2;
}
else
{ b=0;
a=1;
}
Be
Careful
that
you
don't
write
=
when
you
mean
==
Can you see the problem with this code?
if (a=1)
{
42
(AMITY CENTER FOR e-LEARNING)
b =9;
}
This will always set b to 9 as (a=1) is always true. It assigns a value of 1 to a instead of comparing a with
1. The corrected line should be this.
if (a==1)
This is a common mistake amongst beginners.
4.1.1.2 switch statement
The switch statement is much like a nested if .. else statement. Its mostly a matter of preference
which you use, switch statement can be slightly more efficient and easier to read.
switch( expression )
{
case constant-expression1:
statements1;
[case constant-expression2:
statements2;]
[case constant-expression3:
statements3;]
[default : statements4;]
}
When there are many choices, a switch statement may be better than an if. The syntax of a switch
statement is:
switch ( expression) {
case a: statement1; break;
case b: statement2;
case d: statement3; break;
default: statement4;
}
Notes: Expression should be an int or a char type. e.g.
switch (character) {
case 'B':
43
(AMITY CENTER FOR e-LEARNING)
case 'b': statement1; break;
case 'c': statement2;
case 'd': statement3; break;
default: statement4;
}
If the character is 'B' or 'b' then statement1 is executed, but if the character is 'c' then statement2 and
statement3 are both executed. With 'd' though, only statement3 is executed. For all other characters
statement4 is executed.
When a case matches, all statements from that case on, including those that follow are executed until a
break is found. This is known as 'Fall Through'.
switch (value) {
case 3:statement3;
case 2:statement2;
case 1:statement1;
default: break;
}
In this example, I've exploited 'fall through' so that for any number 1-5, statements n; n-1 down to 1 are
executed. Eg if value ==3, statement3, statement2 and statement1 are executed (in that order).
Notes about Switch
Each case can have one or more statements without needing to be enclosed by curly brackets.
The default case can occur anywhere.
Good Practice to use default, perhaps to trap errors.
4.1.2 Looping
Loops provide a way to repeat commands and control how many times they are repeated. C
provides a number of looping way.
44
(AMITY CENTER FOR e-LEARNING)
4.1.2.1 The For Loop
There are several ways of doing loops in C.
The syntax of a for loop is the following:
for (expr-1;expr-2;expr-3)
statement
OR
for ( initial statement; conditional expression; loop statement ) main statement;
Note This is the one place where there is no need to put brackets around a conditional expression. This
executes the initial statement once, then while the conditional expression is true, the loop statement
and the main statement are executed. All three sections are optional so the following is allowed. It is an
infinite loop as the conditional expression is always true if absent.
for ( ;; ) mainstatement;
This will keep executing main statement for ever.
The conditional expression is the most important of the three sections. As long as it is true the main
statement is always run. Once the expression is false, the loop exits.
for (index=1 ;index <= 10;index++ )
printf("The value of i is %i\n\r",i) ;
This outputs ten lines
The value of i is 1
The value of i is 2
..
The value of i is 10
45
(AMITY CENTER FOR e-LEARNING)
index++ is shorthand for adding 1 to index. You could write index = index + 1 or index +=1 but index++ is
more concise.
4.1.2.2 while and do-while
while loop
The most basic loop in C is the while loop.A while statement is like a repeating if statement.
Like an If statement, if the test condition is true: the statments get executed. The difference is
that after the statements have been executed, the test condition is checked again. If it is still true
the statements get executed again.This cycle repeats until the test condition evaluates to false.
Basic syntax of while loop is as follows:
while ( expression )
{
Single statement
or
Block of statements;
}
while
begins by evaluating expression. If it is false, then statement is skipped. If it is true, then
statement is evaluated. Then the expression is evaluated again, and the same check is performed.
The loop exits when expression becomes zero.
One can easily create an infinite loop in C using the while statement:
while (1)
statement
There are two loop statements that use while.
46
(AMITY CENTER FOR e-LEARNING)
First is the while statement which has this syntax.
while (expression) statement
As long as the expression is true, the statement is executed.
int total =0;
int index =0;
while (total < 100)
{
index++;
total+=index;
printf("Sum of 1 to %i is %i",index,total) ;
}
Looping doesn't get much simpler than while. The most important thing to know with while loops is that
the control expression is evaluated before the statement is run. If the expression is false, the statement
is not run. So a while loop may never execute a statement.
Also because c allows a statement to be an expression it is possible to write complicated code like this
int c=10;
int a =0;
while (c--)
{
printf("Value of a is %i",a++) ;
}
c-- subtracts one from c.
Do While
do ... while is just like a while loop except that the test condition is checked at the end of the
loop rather than the start. This has the effect that the content of the loop are always executed at
least once.Less popular than the while statement is the do while statement. This puts the
expression at the end.
47
(AMITY CENTER FOR e-LEARNING)
Basic syntax of do...while loop is as follows:
do
{
Single statement
or
Block of statements;
}while(expression);
A while loop might never execute a statement if the expression is false but a do while will always
execute the statement at least once.
Here is an example of a do while loop.
int count =0;
int index =9;
do
if (value[ index] ==999)
count++;
index--;
while ( index >= 0) ;
Break and Continue work equally well with for, while and do while loops.
Note For and while can be used for same purpose
for loop is similar to while, it's just written differently. for statements are often used to proccess
lists such a range of numbers:
The syntax of a for loop is the following:
for (expr-1;expr-2;expr-3)
statement
48
(AMITY CENTER FOR e-LEARNING)
This is equivalent to the following construct using while:
expr-1;
while (expr-2) {
statement
expr-3;
}
Typically, expr-1 is an assignment, expr-2 is a relational expression, and expr-3 is an increment
or decrement of some manner. For example, the following code counts from 0 to 99, printing
each number along the way:
int i;
for (i= 0; i < 100; i++)
printf("%d\n", i);
4.1.2.3 break and continue statements
C provides two commands to control how we loop:
break -- exit form loop or switch.
continue -- skip 1 iteration of loop.
Break
Use of the break provides an early exit from a while or a for loop.
If a condition is met in switch case then execution continues on into the next case clause also if it
is not explicitly specified that the execution should exit the switch statement. This is achieved by
using break keyword.
Here's an example:
49
(AMITY CENTER FOR e-LEARNING)
int markerpos=-1;
for (index=0 ;index < 10;index++ )
{
if (values[index] ==-999 )
{
markerpos = index;
; break;
}
}
if (markerpos== 999)
printf("-1 Not located in array";
else
printf("-1 Found at index %i\n\r",markerpos) ;
That's a more complicated example. It searches the 10 element array values looking for a 999 value. The
variable markerpos holds the position if a 999 is found. It is initialized to -1 and after the search this
value is examined to see if a 999 was found.
Continue
This does the opposite of break; Instead of terminating the loop, it immediately loops again,
skipping the rest of the code
So lets sum that integer array again, but this time we'll leave out elements with an index of 4 and
5.
int value =0;
for (index=0 ;index < 10;index++ )
{
if (index==4 || index==5)
continue;
value += numbers[index];
50
(AMITY CENTER FOR e-LEARNING)
}
printf("Value = %i",value) ;
You probably won't use continue very often but it's useful on the odd occasion.
You already have seen example of using break statement. Here is an example showing usage of
continue statement.
#include
main()
{
int i;
int j = 10;
for( i = 0; i <= j; i ++ )
{
if( i == 5 )
{
continue;
}
printf("Hello %d\n", i );
}
}
This will produce following output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
51
(AMITY CENTER FOR e-LEARNING)
Hello 6
Hello 7
Hello 8
Hello 9
Hello 10
52
(AMITY CENTER FOR e-LEARNING)
Chapter 5
Functions
Contents:
5.1 What is a function?
5.2 Structure of the function
5.2.1
function header
5.2.2
function body
5.2.3
function prototype
5.3 Need of function
5.4 Parts of function
5.5 Types of function
5.6 Categories of function
5.7 Call by value
5.8 Call by reference
5.9 Recursive function
5.1 What is a Function?
A function is a block of code that has a name and it has a property that it is reusable i.e. it can be
executed from as many different points in a C Program as required. Function groups a number of
program statements into a unit and gives it a name. This unit can be invoked from other parts of a
program. A computer program cannot handle all the tasks by it self. Instead its requests other
program like entities - called functions in C - to get its tasks done. A function is a self contained
block of statements that perform a coherent task of same kind
The name of the function is unique in a C Program and is Global. It neams that a function can be
accessed from any location with in a C Program. We pass information to the function called
arguments specified when the function is called. And the function either returns some value to
the point it was called from or returns nothing. We can divide a long C program into small
blocks which can perform a certain task. A function is a self contained block of statements that
53
(AMITY CENTER FOR e-LEARNING)
perform a coherent task of same kind.Almost all programming languages have some equivalent
of the function. You may have met them under the alternative names subroutine or procedure.
Some languages distinguish between functions which return variables and those which don't. C
assumes that every function will return a value. If the programmer wants a return value, this is
achieved using the return statement. If no return value is required, none should be used when
calling the function
5.2 Structure of a Function
There are two main parts of the function. The function header and the function body.
int sum(int x, int y)
{
int ans = 0;
ans = x + y;
return ans
}
//holds the answer that will be returned
//calculate the sum
//return the answer
Function Header
In the first line of the above code
int sum(int x, int y)
It has three main parts
1.
The name of the function i.e. sum
2.
The parameters of the function enclosed in paranthesis
3.
Return value type i.e. int
Function Body
What ever is written with in { } in the above example is the body of the function.
Function Prototypes
The prototype of a function provides the basic information about a function which tells the
compiler that the function is used correctly or not. It contains the same information as the
function header contains. The prototype of the function in the above example would be like
54
(AMITY CENTER FOR e-LEARNING)
int sum (int x, int y);
The only difference between the header and the prototype is the semicolon ; there must the a
semicolon at the end of the prototype.
5.3 Need of functions
The basic philosophy of function is divide and conquer by which a complicated tasks are
successively divided into simpler and more manageable tasks which can be easily handled. A
program can be divided into smaller subprograms that can be developed and tested successfully.
A function is a complete and independent program which is used (or invoked) by the main
program or other subprograms. A subprogram receives values called arguments from a calling
program, performs calculations and returns the results to the calling program.
There are many advantages in using functions in a program they are:
1. It facilitates top down modular programming. In this programming style, the high level logic
of the overall problem is solved first while the details of each lower level functions is addressed
later.
2. the length of the source program can be reduced by using functions at appropriate places. This
factor is critical with microcomputers where memory space is limited.
3. It is easy to locate and isolate a faulty function for further investigation.
4. A function may be used by many other programs this means that a c programmer can build on
what others have already done, instead of starting over from scratch.
5. A program can be used to avoid rewriting the same sequence of code at two or more locations
in a program. This is especially useful if the code involved is long or complicated.
6. Programming teams does a large percentage of programming. If the program is divided into
subprograms, each subprogram can be written by one or two team members of the team rather
than having the whole team to work on the complex program
55
(AMITY CENTER FOR e-LEARNING)
7. We can understand the flow of program, and its code easily since the readability is enhanced
while using the functions
5.4 Parts of a function
Each function used in a program must be
Declared : it is like registering the function to make itself known for use
Defined
: related with actual working or functionality of fuinction
Called : to make function in use
Function Declaration
Just as you can‖t use a variable without first telling the compler what it is, you also can‖t use a
funciotns without teling the compiler about it, There are two ways to do this . The approach we
show here ist o declare the funtion before it is called. The other approach is to define it before
it‖s called. ; we‖ll examine that next.) in the Table program, the functions starline() is declared in
the line.
Void starline ( );
The declaration tells the compiler that at some later point we plan to present a function called
starline. The keyword void specifies that the function has no return value, and the empty
parentheses indicate that it takes no arguments.
Notice that the functions declarations is terminated with a semicolon It is a complete statement in
itself.
Function declarations are also called prototypes, since they provide a model or blueprint for the
function. They tell the compiler,‖ a function that looks like this is coming up later in the
program, so it‖s all right if you see references to it before you see the function itself.‖
Calling the Function
56
(AMITY CENTER FOR e-LEARNING)
The function is called (or invoked) three times from main (). Each of the three calls look like
this:
Starline():
This is all we need to call a function name, followed by parentheses. The syntax of the call is
very similar to that of declaration, except that the return type is not used. A semicolon terminates
the call. Executing the call statement causes the function to execute; that is, control is transferred
to the function, the statement in the function definition are executed, and then control returns to
the statement following the function call.
Function definition
[ data type] function name (argument list)
argument declaration;
{
local variable declarations;
statements;
[return expression]
}
Example :
mul(a,b)
int a,b;
{
int y;
y=a+b;
return y;
}
When the value of y which is the addition of the values of a and b. the last two statements ie,
y=a+b; can be combined as
57
(AMITY CENTER FOR e-LEARNING)
return(y)
return(a+b);
5.5 Types of Function
There are basically two types of functions
1.
Library functions Ex. printf ( ), scanf ( ) etc.
2.
User defined function e.g the function message mentioned above.
C support the use of library functions and use defined functions. The library functions are used
to carry out a number of commonly used operations or calculations. The user-defined functions
are written by the programmer to carry out various individual tasks The following point must be
noted about functions
(i)
C program is a collection of one or more functions
(ii)
A function gets called when the function name is followed by a semicolon for e.g.
main ( )
{
message ( );
}
3.
Function is defined when function name is followed by a pair of braces in which one or
more statements may be present for e.g.
message ( )
{
statement 1;
58
(AMITY CENTER FOR e-LEARNING)
statement2;
statement 3;
}
4.
Any function can be called from any other function even main ( ) can be called from
other functions. for e.g.
main ( )
{
message ( );
}
message ( )
{
printf (‖ \n Hello‖);
main ( );
}
5.
A function can be called any number of times for eg.
main ()
{
message ( );
message ( );
}
59
(AMITY CENTER FOR e-LEARNING)
message ( )
{
printf (‖\n Hello‖);
}
6.
The order in which the functions are defined in a program and the order in which they get
called need not necessarily be same for e.g.
main ( );
{
message 1 ( );
message 2 ( );
}
message 2 ( )
{
printf (‖\n I am learning C‖);
}
message 1 ( )
{
printf ( ―\n Hello ―);
}
7.
A function can call itself such a process as called ―recursion‖.
60
(AMITY CENTER FOR e-LEARNING)
8.
A function can be called from other function, but a function cannot be defined in an-other
function. The following program code would be wrong, since Argentina is being defined
inside another function main ( ).
main ( )
{
printf (‖\n I am in main‖);
argentina ( )
{
printf {‖\n I am in argentina‖);
}
}
9.
Any C program contains at least one function.
10.
If a program contains only one function, it must be main( ).
11.
In a C program if there are more than one functional present then one of these func-tional
must be main( ) because program execution always begins with main( ).
12.
There is no limit on the number of functions that might be present in a C program.
13.
Each function in a program is called in the sequence specified by the function calls in
main( )
14.
After each function has done its thing, control returns to the main( ), when main( ) runs
out of function calls, the program ends.
Functions declaration and prototypes Any function by default returns an int value. If we desire
that a function should return a value other than an int, then it is necessary to explicitly mention
so in the calling functions as well as in the called function. for e.g
main ( )
61
(AMITY CENTER FOR e-LEARNING)
{
float a,b,
printf ("\n Enter any number");
scanf ("\% f", &a );
b = square (a)
printf ("\n square of % f is % f", a,b);
}
square (float X)
{
float y;
Y = x * x;
return (y);
}
the sample run of this program is Enter any number 2.5 square of 2.5 is 6.000000 Here 6 is not a
square of 2.5 this happened because any C function, by default, always returns an integer value.
The following program segment illustrates how to make square ( ) capable of returning a float
value.
main ( )
{
float square ( );
float a, b;
printf ("\n Enter any number ");
scanf ("%f" &a);
b = square (a);
printf ("\n square of % f is % f, " a,
b);
}
62
(AMITY CENTER FOR e-LEARNING)
float square (float x)
{
float y;
y= x *x;
return ( y);
}
5.6 Categories of functions
A function may belong to any one of the following categories:
1. Functions with no arguments and no return values.
2. Functions with arguments and no return values.
3. Functions with arguments and return values.
Functions with no arguments and no return values:
Let us consider the following program
/* Program to illustrate a function with no argument and no return values*/
#include
main()
{
staetemtn1();
starline();
statement2();
starline();
}
/*function to print a message*/
statement1()
{
printf(―\n Sample subprogram output‖);
63
(AMITY CENTER FOR e-LEARNING)
}
statement2()
{
printf(―\n Sample subprogram output two‖);
}
starline()
{
int a;
for (a=1;a<60;a++)
printf(―%c‖,‘*‘);
printf(―\n‖);
}
In the above example there is no data transfer between the calling function and the called
function. When a function has no arguments it does not receive any data from the calling
function. Similarly when it does not return value the calling function does not receive any data
from the called function. A function that does not return any value cannot be used in an
expression it can be used only as independent statement.
Functions with arguments but no return values:
The nature of data communication between the calling function and the arguments to the called
function and the called function does not return any values to the calling function this shown in
example below:
Consider the following: Function calls containing appropriate arguments. For example the
function call
value (500,0.12,5)
Would send the values 500,0.12 and 5 to the function value (p, r, n) and assign values 500 to p,
0.12 to r and 5 to n. the values 500,0.12 and 5 are the actual arguments which become the values
of the formal arguments inside the called function. Both the arguments actual and formal should
64
(AMITY CENTER FOR e-LEARNING)
match in number type and order. The values of actual arguments are assigned to formal
arguments on a one to one basis starting with the first argument as shown below:
main()
{
function1(a1,a2,a3……an)
}
function1(f1,f2,f3….fn);
{
function
body;
}
here a1,a2,a3 are actual arguments and f1,f2,f3 are formal arguments.
The no of formal arguments and actual arguments must be matching to each other suppose if
actual arguments are more than the formal arguments, the extra actual arguments are discarded.
If the number of actual arguments are less than the formal arguments then the unmatched formal
arguments are initialized to some garbage values. In both cases no error message will be
generated. The formal arguments may be valid variable names, the actual arguments may be
variable names expressions or constants. The values used in actual arguments must be assigned
values before the function call is made. When a function call is made only a copy of the values
actual arguments is passed to the called function. What occurs inside the functions will have no
effect on the variables used in the actual argument list.
Let us consider the following program
/*Program to find the largest of two numbers using function*/
#include
main()
{
int a,b;
65
(AMITY CENTER FOR e-LEARNING)
printf(―Enter the two numbers‖);
scanf(―%d%d‖,&a,&b);
largest(a,b)
}
/*Function to find the largest of two numbers*/
largest(int a, int b)
{
if(a>b)
printf(―Largest element=%d‖,a);
else
printf(―Largest element=%d‖,b);
}
in the above program we could make the calling function to read the data from the terminal and
pass it on to the called function. But function foes not return any value.
Functions with arguments and return values:
The function of the type Arguments with return values will send arguments from the calling
function to the called function and expects the result to be returned back from the called function
back to the calling function. To assure a high degree of portability between programs a function
should generally be coded without involving any input output operations. For example different
programs may require different output formats for displaying the results. Theses shortcomings
can be overcome by handing over the result of a function to its calling function where the
returned value can be used as required by the program. In the above type of function the
following steps are carried out:
1. The function call transfers the controls along with copies of the values of the actual arguments
of the particular function where the formal arguments are creates and assigned memory space
and are given the values of the actual arguments.
66
(AMITY CENTER FOR e-LEARNING)
2. The called function is executed line by line in normal fashion until the return statement is
encountered. The return value is passed back to the function call is called function.
3. The calling statement is executed normally and return value is thus assigned to the calling
function.
Note that the value return by any function when no format is specified is an integer.
Return value data type of function:
A C function returns a value of type int as the default data type when no other type is specified
explicitly. For example if function does all the calculations by using float values and if the return
statement such as return (sum); returns only the integer part of the sum. This is since we have not
specified any return type for the sum. There is the necessity in some cases it is important to
receive float or character or double data type. To enable a calling function to receive a noninteger value from a called function we can do the two things:
1. The explicit type specifier corresponding to the data type required must be mentioned in the
function header. The general form of the function definition is
Type_specifier function_name(argument list)
Argument declaration;
{
function statement;
}
The type specifier tells the compiler, the type of data the function is to return.
2. The called function must be declared at the start of the body in the calling function, like any
other variable. This is to tell the calling function the type of data the function is actually
returning. The program given below illustrates the transfer of a floating-point value between
functions done in a multiple function program.
67
(AMITY CENTER FOR e-LEARNING)
main()
{
float x,y,add();
double sub(0;
x=12.345;
y=9.82;
printf(―%f\n‖ add(x,y));
printf(―%lf\n‖sub(x,y);
}
float add(a,b)
float a,b;
{
return(a+b);
}
double sub(p,q)
double p,q;
{
return(p-q);
}
We can notice that the functions too are declared along with the variables. These declarations
clarify to the compiler that the return type of the function add is float and sub is double.
Void functions:
The functions that do not return any values can be explicitly defined as void. This prevents any
accidental use of these functions in expressions.
5.7 CALL BY VALUE
In the preceding examples we have seen that whenever we called a function we have always
passed the values of variables to the called function. Such function calls are called ―calls by
68
(AMITY CENTER FOR e-LEARNING)
value‖ by this what it meant is that on calling a function we are passing values of variables to it.
The example of call by value is shown below;
sum = calsum (a, b, c); f = factr (a);
In this method the value of each of the actual arguments in the calling function is copied into
corresponding formal arguments of the called function. With this method the changes made to
the formal arguments in the called function have no effect on the values of actual argument in the
calling function. The following program illustrates this:
main ( )
{
int a = 10, b=20;
swapy (a,b);
printf ("\na = % d b = % d", a,b);
}
swapy (int x, int y)
{
int t;
t = x;
x = y;
y = t;
printf ( "\n x = % d y = % d" , x, y);
}
The output of the above program would be; x = 20 y = 10 a =10 b =20
5.8 CALL BY REFERENCE
In the second method the addresses of actual arguments in the calling function are copied in to
formal arguments of the called function. This means that using these addresses we would have an
access to the actual arguments and hence we would be able to manipulate them the following
program illustrates this.
69
(AMITY CENTER FOR e-LEARNING)
main ( )
{
int a = 10, b =20,
swapv (&a, &b);
printf ("\n a = %d b= %d", a, b);
}
swapr (int **, int * y)
{
int t;
t = *x
*x = *y;
*y = t;
}
The output of the above program would be a = 20 b =10
5.9 Recursive Functions
A recursive function is one which calls itself. This is another complicated idea which you are
unlikely to meet frequently. We shall provide some examples to illustrate recursive functions.
Recursive functions are useful in evaluating certain types of mathematical function. You may
also encounter certain dynamic data structures such as linked lists or binary trees. Recursion is a
very useful way of creating and accessing these structures.
Here is a recursive version of the Fibonacci function.
int fib(int num)
/* Fibonacci value of a number */
{
switch(num) {
case 0:
return(0);
break;
70
(AMITY CENTER FOR e-LEARNING)
case 1:
return(1);
break;
default: /* Including recursive calls */
return(fib(num - 1) + fib(num - 2));
break;
}
}
We met another function earlier called power. Here is an alternative recursive version.
double power(double val, unsigned pow)
{
if(pow == 0) /* pow(x, 0) returns 1 */
return(1.0);
else
return(power(val, pow - 1) * val);
}
Notice that each of these definitions incorporates a test. Where an input value gives a trivial
result, it is returned directly, otherwise the function calls itself, passing a changed version of the
input values. Care must be taken to define functions which will not call themselves indefinitely,
otherwise your program will never finish. The definition of fib is interesting, because it calls
itself twice when recursion is used. Consider the effect on program performance of such a
function calculating the fibonacci function of a moderate size number.
71
(AMITY CENTER FOR e-LEARNING)
If such a function is to be called many times, it is likely to have an adverse effect on program
performance. Don't be frightened by the apparent complexity of recursion. Recursive functions
are sometimes the simplest answer to a calculation. However there is always an alternative nonrecursive solution available too. This will normally involve the use of a loop, and may lack the
elegance of the recursive solution. As the definition specifies, there are two types of recursive
functions. Consider a function which calls itself: we call this type of recursion immediate
recursion.
One can view this mathematically in a directed call graph.
A -- -|
^
|
|
|
|---- |
void A() {
A();
return;
}
A() is a recursive function since it directly calls itself.
The second part of the defintion refers to a cycle (or potential cycle if we use conditional
statements) which involves other functions.
Consider the following directed call graph
72
(AMITY CENTER FOR e-LEARNING)
A ---------> B
^
|
|
|
|
|
|---- C <----|
This can be viewed in the following three functions:
void C() {
A();
return;
}
void B() {
C();
return;
}
void A() {
B();
return;
}
Recursive functions are an inefficient means of solving problems in terms of run times but are
interesting to study nonetheless. For our purposes we will only consider immediate recursion
since this will cause enough difficulty.
Writing Recursive Functions
A recursive function has the following general form (it is simply a specification of the general
function we have seen many times):
ReturnType Function( Pass appropriate arguments ) {
if a simple case, return the simple value // base case / stopping condition
else call function with simpler version of problem
}
For a recursive function to stop calling itself we require some type of stopping condition. If it is
not the base case, then we simplify our computation using the general formula.
73
(AMITY CENTER FOR e-LEARNING)
Chapter 6
Arrays
Contents:
6.1 Introduction
6.2 Need of an array
6.3 Declaration of Arrays
6.4 Initialization of an arrays
6.5 More operations on array
6.5.1
Create an array
6.5.2
Printing an array
6.5.3
Copying an array
6.6 Multidimensional Array
6.6.1
initialization of multidimensional array
6.6.2
more on multidimensional array
6.1 Introduction
The C language provides a capability that enables the user to define a set of ordered data items
known as an array. Alternatively, array is a collection of homogeneous element, ,homogeneous
means elements of same data type .Suppose we had a set of grades that we wished to read into
the computer and suppose we wished to perform some operations on these grades, we will
quickly realize that we cannot perform such an operation until each and every grade has been
entered since it would be quite a tedious task to declare each and every student grade as a
variable especially since there may be a very large number.
In C we can define variable called grades, which represents not a single value of grade but a
entire set of grades. Each element of the set can then be referenced by means of a number called
as index number or subscript.
6.2 Need of an Array
To understand the need , Let's start by looking at a single variable used to store a person's age.
74
(AMITY CENTER FOR e-LEARNING)
1: #include <stdio.h>
2:
3: int main()
4: {
5: short age;
6: age=23;
7: printf("%d\n", age);
8: return 0;
9: }
Not much to it. The variable age is created at line (5) as a short. A value is assigned to age.
Finally, age is printed to the screen.
Now let's keep track of 4 ages instead of just one. We could create 4 separate variables, but 4
separate variables have limited appeal. (If using 4 separate variables is appealing to you, then
consider keeping track of 93843 ages instead of just 4). Rather than using 4 separate variables,
we'll use an array. Hence the need
6.3
Declaration of arrays
Like any other variable arrays must be declared before they are used. The general form of
declaration is:
type variable-name[50];
The type specifies the type of the elements that will be contained in the array, such as int float or
char and the size indicates the maximum number of elements that can be stored inside the array
for ex: float height[50];
Declares the height to be an array containing 50 real elements. Any subscripts 0 to 49 are valid.
In C the array elements index or subscript begins with number zero. So height [0] refers to the
first element of the array. (For this reason, it is easier to think of it as referring to element
number zero, rather than as referring to the first element). As individual array element can be
used anywhere that a normal variable with a statement such as
G = grade [50];
The statement assigns the value stored in the 50th index of the array to the variable g.
More generally if I is declared to be an integer variable, then the statement g=grades [I];
Will take the value contained in the element number I of the grades array to assign it to g. so if I
were equal to 7 when the above statement is executed, then the value of grades [7] would get
assigned to g. A value stored into an element in the array simply by specifying the array element
on the left hand side of the equals sign. In the statement
grades [100]=95;
75
(AMITY CENTER FOR e-LEARNING)
The value 95 is stored into the element number 100 of the grades array.
The ability to represent a collection of related data items by a single array enables us to develop
concise and efficient programs. For example we can very easily sequence through the elements
in the array by varying the value of the variable that is used as a subscript into the array. So the
for loop
for(i=0;i < 100;++i);
sum = sum + grades [i];
Will sequence through the first 100 elements of the array grades (elements 0 to 99) and will add
the values of each grade into sum. When the for loop is finished, the variable sum will then
contain the total of first 100 values of the grades array (Assuming sum were set to zero before
the loop was entered) In addition to integer constants, integer valued expressions can also be
inside the brackets to reference a particular element of the array. So if low and high were defined
as integer variables, then the statement
next_value=sorted_data[(low+high)/2];
would assign to the variable next_value indexed by evaluating the expression (low+high)/2. If
low is equal to 1 and high were equal to 9, then the value of sorted_data[5] would be assigned to
the next_value and if low were equal to 1 and high were equal to 10 then the value of
sorted_data[5] would also be referenced.
Just as variables arrays must also be declared before they are used. The declaration of an array
involves the type of the element that will be contained in the array such as int, float, char as well
as maximum number of elements that will be stored inside the array. The C system needs this
latter information in order to determine how much memory space to reserve for the particular
array.The declaration int values[10]; would reserve enough space for an array called values that
could hold up to 10 integers. Refer to the below given picture to conceptualize the reserved
storage space.
values[0]
values[1]
values[2]
values[3]
76
(AMITY CENTER FOR e-LEARNING)
values[4]
values[5]
values[6]
values[7]
values[8]
values[9]
The array values stored in the memory.
6.4
Initialization of arrays
We can initialize the elements in the array in the same way as the ordinary variables when they
are declared. The general form of initialization off arrays is:
type array_name[size]={list of values};
The values in the list care separated by commas, for example the statement
int number[3]={0,0,0};
Will declare the array size as a array of size 3 and will assign zero to each element if the number
of values in the list is less than the number of elements, then only that many elements are
initialized. The remaining elements will be set to zero automatically.
In the declaration of an array the size may be omitted, in such cases the compiler allocates
enough space for all initialized elements. For example the statement
int counter[]={1,1,1,1};
Will declare the array to contain four elements with initial values 1. this approach works fine as
long as we initialize every element in the array.
The
initialization
of
arrays
in
c
suffers
two
draw
backs
1.There is no convenient way to initialize only selected elements.
2. There is no shortcut method to initialize large number of elements.
77
(AMITY CENTER FOR e-LEARNING)
6.5 More operations on array
6.5.1 Create an Array in C
Step 1 Understand that every element of an array (or any other kind of list) will be the same
kind of data. For example, a list of test scores will be an array of integers, since each test score is
an integer. For more complex data, you will need to define a structure.
Step 2 Decide what the largest size your array can ever reach will be, because arrays have a
fixed length. You'll want to strike a balance between having your program able to handle
unusually long data sets, and having it require (and waste) a lot of memory.
Step 3 Create the array the same way you would create a single variable, but add the maximum
size in square brackets after the name, as in these examples:
int test_scores[50];
char first_name[20];
employee_record employees[1000];
Step 4 Create a second variable that keeps track of how many elements you've added to the array
so far.
Step 5 Create an array with multiple dimensions simply making an array of arrays, like this:
int test_answers[10][20];
char student_names[50][20];
Use the Array
Step 7 Access the array's elements using the index in square brackets, like this:
test_scores[12] = 50;
printf("Test score: %d\n", test_scores[i]);
78
(AMITY CENTER FOR e-LEARNING)
Step 8 Pass arrays to functions, if you so choose. You don't need to (and should not) specify the
size in the function definition. This is what the function definition and call would look like:
function subtotal(int test_scores[]);
test_subtotal = subtotal(test_scores);
6.5.2 Printing arrays
Here is the same program with an attempt to print the array to the screen:
1: #include <stdio.h>
2:
3: int main()
4: {
5: short age[4];
6: age[0]=23;
7: age[1]=34;
8: age[2]=65;
9: age[3]=74;
10:
11: printf("%x\n", age);
12: return 0;
13: }
Line (11) is meant to print the 4 ages to the screen. But instead of printing out the four short
variables, what appears to be nonsense prints out instead. The important point to come away with
is that simply providing the name of the array in an output statement will not print out the
elements of the array.
How about printing out each of the values separately? Try this:
1: #include <stdio.h>
2:
79
(AMITY CENTER FOR e-LEARNING)
3: int main()
4: {
5: short age[4];
6: age[0]=23;
7: age[1]=34;
8: age[2]=65;
9: age[3]=74;
10: printf("%d\n", age[0]);
11: printf("%d\n", age[1]);
12: printf("%d\n", age[2]);
13: printf("%d\n", age[3]);
14: return 0;
15: }
Lines (10) through line (13) produce the output we are expecting.
There is no single statement in the language that says "print an entire array to the screen". Each
element in the array must be printed to the screen individually.
6.5.3 Copying arrays
Suppose that after filling our 4 element array with values, we need to copy that array to another
array of 4 short's? Try this:
1: #include <stdio.h>
2:
3: int main()
4: {
5: short age[4];
6: short same_age[4];
80
(AMITY CENTER FOR e-LEARNING)
7: age[0]=23;
8: age[1]=34;
9: age[2]=65;
10: age[3]=74;
11:
12: same_age=age;
13:
14: printf("%d\n", same_age[0]);
15: printf("%d\n", same_age[1]);
16: printf("%d\n", same_age[2]);
17: printf("%d\n", same_age[3]);
18: return 0;
19: }
Line (12) tries to copy the age array into the same_age array. What happened when you tried to
compile the program above?
Let's try copying arrays using a technique similar to the technique used to print arrays (that is,
one element at a time):
1: #include <stdio.h>
2:
3: int main()
4: {
5: short age[4];
6: short same_age[4];
7:
8: age[0]=23;
9: age[1]=34;
10: age[2]=65;
11: age[3]=74;
12:
81
(AMITY CENTER FOR e-LEARNING)
13: same_age[0]=age[0];
14: same_age[1]=age[1];
15: same_age[2]=age[2];
16: same_age[3]=age[3];
17:
18: printf("%d\n", same_age[0]);
19: printf("%d\n", same_age[1]);
20: printf("%d\n", same_age[2]);
21: printf("%d\n", same_age[3]);
22: return 0;
23: }
This technique for copying arrays works fine. Two arrays are created: age and same_age. Each
element of the age array is assigned a value. Then, in order to copy of the four elements in age
into the same_age array, we must do it element by element. Like printing arrays, there is no
single statement in the language that says "copy an entire array into another array". The
array elements must be copied individually. The technique used to copy one array into another
is exactly the same as the technique used to copy 4 separate variables into 4 other variables. So
what is the advantage to using arrays over separate variables? One significant advantage of an
array over separate variables is the name. In our examples, using four separate variables requires
4 unique names. The 4 short variables in our array have the same name, age. The 4 short's in the
array are identical except for an index number used to access them. This distinction allows us to
shorten our code in a way that would be impossible with 4 variables, each with unique names:
1: #include <stdio.h>
2:
3: int main()
4: {
5: short age[4];
6: short same_age[4];
7: int i, j;
82
(AMITY CENTER FOR e-LEARNING)
8: age[0]=23;
9: age[1]=34;
10: age[2]=65;
11: age[3]=74;
12:
13: for(i=0; i<4; i++)
14:
same_age[i]=age[i];
15:
16: for(j=0; j<4; j++)
17:
printf("%d\n", same_age[j]);
18: return 0;
19: }
Since the only difference between each of the short's in the arrays is their index, a loop and a
counter can be used to more easily copy all of the elements. The same technique is used to
shorten the code that prints the array to the screen. Even though arrays give us some convenience
when managing many variables of the same type, there is little difference between an array and
variables declared individually. There is no single statement to copy an array, there is no single
statement to print an array.
If we want to perform any action on an array, we must repeatedly perform that action on
each element in the array.
6.6 Multi dimensional Arrays
Often there is a need to store and manipulate two dimensional data structure such as matrices &
tables. Here the array has two subscripts. One subscript denotes the row & the other the column.
The declaration of two dimension arrays is as follows:
data_type array_name[row_size][column_size];
int m[10][20]
83
(AMITY CENTER FOR e-LEARNING)
Here m is declared as a matrix having 10 rows( numbered from 0 to 9) and 20
columns(numbered 0 through 19). The first element of the matrix is m[0][0] and the last row last
column is m[9][19]
Elements of multi dimension arrays:
A 2 dimensional array marks [4][3] is shown below figure. The first element is given by marks
[0][0] contains 35.5 & second element is marks [0][1] and contains 40.5 and so on.
marks [0][0] Marks [0][1] Marks [0][2]
35.5
40.5
45.5
marks [1][0] Marks [1][1] Marks [1][2]
50.5
55.5
60.5
marks [2][0] Marks [2][1] Marks [2][2]
marks [3][0] Marks [3][1] Marks [3][2]
6.6.1 Initialization of multidimensional arrays
Like the one dimension arrays, 2 dimension arrays may be initialized by following their
declaration with a list of initial values enclosed in braces
Example:
int table[2][3]={0,0,01,1,1};
Initializes the elements of first row to zero and second row to 1. The initialization is done row by
row. The above statement can be equivalently written as
int table[2][3]={{0,0,0},{1,1,1}}
By surrounding the elements of each row by braces. C allows arrays of three or more
dimensions. The compiler determines the maximum number of dimension. The general form of a
multidimensional array declaration is:
84
(AMITY CENTER FOR e-LEARNING)
date_type array_name[s1][s2][s3]…..[sn];
Where s is the size of the ith dimension. Some examples are:
int survey[3][5][12];
float table[5][4][5][3];
Survey is a 3 dimensional array declared to contain 180 integer elements. Similarly table is a
four dimensional array containing 300 elements of floating point type.
/* example program to add two matrices & store the results in the 3rd matrix */
#include< stdio.h >
#include< conio.h >
void main()
{
int a[10][10],b[10][10],c[10][10],i,j,m,n,p,q;
clrscr();
printf(―enter the order of the matrix\n‖);
scanf(―%d%d‖,&p,&q);
if(m==p && n==q)
{
printf(―matrix can be added\n‖);
printf(―enter the elements of the matrix a‖);
for(i=0;i < m;i++)
for(j=0;j < n;j++)
scanf(―%d‖,&a[i][j]);
printf(―enter the elements of the matrix b‖);
for(i=0;i < p;i++)
for(j=0;j < q;j++)
scanf(―%d‖,&b[i][j]);
printf(―the sum of the matrix a and b is‖);
for(i=0;i < m;i++)
for(j=0;j < n;j++)
85
(AMITY CENTER FOR e-LEARNING)
c[i][j]=a[i][j]+b[i][j];
for(i=0;i < m;i++)
{
for(j=0;j < n;j++)
printf(―%d\t‖,&a[i][j]);
printf(―\n‖);
}
}
6.6.2 More on Multi-Dimensional Arrays
consider
#define ROWS 5
#define COLS 10
int multi[ROWS][COLS];
we can access individual elements of the array multi using either:
multi[row][col]
or
*(*(multi + row) + col)
To understand more fully what is going on, let us replace
*(multi + row)
with X as in:
*(X + col)
Now, from this we see that X is like a pointer since the expression is de-referenced and we know
that col is an integer. Here the arithmetic being used is of a special kind called "pointer
arithmetic" is being used. That means that, since we are talking about an integer array, the
address pointed to by (i.e. value of) X + col + 1 must be greater than the address X + col by and
amount equal to sizeof(int). Since we know the memory layout for 2 dimensional arrays, we can
determine that in the expression multi + row as used above, multi + row + 1 must increase by
value an amount equal to that needed to "point to" the next row, which in this case would be an
86
(AMITY CENTER FOR e-LEARNING)
amount equal to COLS * sizeof(int). That says that if the expression *(*(multi + row) + col) is
to be evaluated correctly at run time, the compiler must generate code which takes into
consideration the value of COLS, i.e. the 2nd dimension. Because of the equivalence of the two
forms of expression, this is true whether we are using the pointer expression as here or the array
expression multi[row][col].
Thus, to evaluate either expression, a total of 5 values must be known:
1. The address of the first element of the array, which is returned by the expression multi,
i.e., the name of the array.
2. The size of the type of the elements of the array, in this case sizeof(int).
3. The 2nd dimension of the array
4. The specific index value for the first dimension, row in this case.
5. The specific index value for the second dimension, col in this case.
Given all of that, consider the problem of designing a function to manipulate the element values
of a previously declared array. For example, one which would set all the elements of the array
multi to the value 1.
void set_value(int m_array[][COLS])
{
int row, col;
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
m_array[row][col] = 1;
}
}
}
And to call this function we would then use:
87
(AMITY CENTER FOR e-LEARNING)
set_value(multi);
Now, within the function we have used the values #defined by ROWS and COLS that set the
limits on the for loops. But, these #defines are just constants as far as the compiler is concerned,
i.e. there is nothing to connect them to the array size within the function. row and col are local
variables, of course. The formal parameter definition permits the compiler to determine the
characteristics associated with the pointer value that will be passed at run time. We really don‘t
need the first dimension and, as will be seen later, there are occasions where we would prefer not
to define it within the parameter definition, out of habit or consistency, I have not used it here.
But, the second dimension must be used as has been shown in the expression for the parameter.
The reason is that we need this in the evaluation of m_array[row][col] as has been described.
While the parameter defines the data type (int in this case) and the automatic variables for row
and column are defined in the for loops, only one value can be passed using a single parameter.
In this case, that is the value of multi as noted in the call statement, i.e. the address of the first
element, often referred to as a pointer to the array. Thus, the only way we have of informing the
compiler of the 2nd dimension is by explicitly including it in the parameter definition.
In fact, in general all dimensions of higher order than one are needed when dealing with multidimensional arrays. That is if we are talking about 3 dimensional arrays, the 2nd and 3rd
dimension must be specified in the parameter definition.
88
(AMITY CENTER FOR e-LEARNING)
Chapter 7
Pointers
Contents:
7.1 Introduction
7.2 Pointer types and Arrays
7.3 Pointers and Strings
7.4 Pointers to Arrays
7.5 Dynamic Allocation of Memory
7.1 introduction
One of those things beginners in C find difficult is the concept of pointers. The purpose of this
chapter is to provide an introduction to pointers and their use to these beginners. I have found
that often the main reason beginners have a problem with pointers is that they have a weak or
minimal feeling for variables, (as they are used in C). Thus we start with a discussion of C
variables in general.A variable in a program is something with a name, the value of which can
vary. The way the compiler and linker handles this is that it assigns a specific block of memory
within the computer to hold the value of that variable. The size of that block depends on the
range over which the variable is allowed to vary. For example, on 32 bit PC's the size of an
integer variable is 4 bytes. On older 16 bit PCs integers were 2 bytes. In C the size of a variable
type such as an integer need not be the same on all types of machines. Further more there is
more than one type of integer variable in C. We have integers, long integers and short integers
which you can read up on in any basic text on C. This document assumes the use of a 32 bit
system with 4 byte integers.
If you want to know the size of the various types of integers on your system, running the
following code will give you that information.
#include <stdio.h>
89
(AMITY CENTER FOR e-LEARNING)
int main()
{
printf("size of a short is %d\n", sizeof(short));
printf("size of a int is %d\n", sizeof(int));
printf("size of a long is %d\n", sizeof(long));
}
When we declare a variable we inform the compiler of two things, the name of the variable and
the type of the variable. For example, we declare a variable of type integer with the name k by
writing:
int k;
On seeing the "int" part of this statement the compiler sets aside 4 bytes of memory (on a PC) to
hold the value of the integer. It also sets up a symbol table. In that table it adds the symbol k and
the relative address in memory where those 4 bytes were set aside. Thus, later if we write:
k = 2;
we expect that, at run time when this statement is executed, the value 2 will be placed in that
memory location reserved for the storage of the value of k. In C we refer to a variable such as the
integer k as an "object". In a sense there are two "values" associated with the object k. One is the
value of the integer stored there (2 in the above example) and the other the "value" of the
memory location, i.e., the address of k. Some texts refer to these two values with the
nomenclature rvalue (right value, pronounced "are value") and lvalue (left value, pronounced "el
value") respectively.
In some languages, the lvalue is the value permitted on the left side of the assignment operator '='
(i.e. the address where the result of evaluation of the right side ends up). The rvalue is that which
is on the right side of the assignment statement, the 2 above. Rvalues cannot be used on the left
side of the assignment statement. Thus: 2 = k; is illegal. Actually, the above definition of
"lvalue" is somewhat modified for C. According to K&R II
"An object is a named region of storage; an lvalue is an expression referring to an object."
90
(AMITY CENTER FOR e-LEARNING)
However, at this point, the definition originally cited above is sufficient. As we become more
familiar with pointers we will go into more detail on this.
Okay, now consider:
int j, k;
k = 2;
j = 7;
<-- line 1
k = j;
<-- line 2
In the above, the compiler interprets the j in line 1 as the address of the variable j (its lvalue) and
creates code to copy the value 7 to that address. In line 2, however, the j is interpreted as its
rvalue (since it is on the right hand side of the assignment operator '='). That is, here the j refers
to the value stored at the memory location set aside for j, in this case 7. So, the 7 is copied to the
address designated by the lvalue of k. In all of these examples, we are using 4 byte integers so all
copying of rvalues from one storage location to the other is done by copying 4 bytes. Had we
been using two byte integers, we would be copying 2 bytes.
Now, let's say that we have a reason for wanting a variable designed to hold an lvalue (an
address). The size required to hold such a value depends on the system. On older desk top
computers with 64K of memory total, the address of any point in memory can be contained in 2
bytes. Computers with more memory would require more bytes to hold an address. The actual
size required is not too important so long as we have a way of informing the compiler that what
we want to store is an address. Such a variable is called a pointer variable (for reasons which
hopefully will become clearer a little later). In C when we define a pointer variable we do so by
preceding its name with an asterisk. In C we also give our pointer a type which, in this case,
refers to the type of data stored at the address we will be storing in our pointer. For example,
consider the variable declaration:
int *ptr;
ptr is the name of our variable (just as k was the name of our integer variable). The '*' informs
the compiler that we want a pointer variable, i.e. to set aside however many bytes is required to
store an address in memory. The int says that we intend to use our pointer variable to store the
91
(AMITY CENTER FOR e-LEARNING)
address of an integer. Such a pointer is said to "point to" an integer. However, note that when we
wrote int k; we did not give k a value. If this definition is made outside of any function ANSI
compliant compilers will initialize it to zero. Similarly, ptr has no value, that is we haven't stored
an address in it in the above declaration. In this case, again if the declaration is outside of any
function, it is initialized to a value guaranteed in such a way that it is guaranteed to not point to
any C object or function. A pointer initialized in this manner is called a "null" pointer.
The actual bit pattern used for a null pointer may or may not evaluate to zero since it depends on
the specific system on which the code is developed. To make the source code compatible
between various compilers on various systems, a macro is used to represent a null pointer. That
macro goes under the name NULL. Thus, setting the value of a pointer using the NULL macro,
as with an assignment statement such as ptr = NULL, guarantees that the pointer has become a
null pointer. Similarly, just as one can test for an integer value of zero, as in if(k == 0), we can
test for a null pointer using if (ptr == NULL).
But, back to using our new variable ptr. Suppose now that we want to store in ptr the address of
our integer variable k. To do this we use the unary & operator and write:
ptr = &k;
What the & operator does is retrieve the lvalue (address) of k, even though k is on the right hand
side of the assignment operator '=', and copies that to the contents of our pointer ptr. Now, ptr is
said to "point to" k. Bear with us now, there is only one more operator we need to discuss.
The "dereferencing operator" is the asterisk and it is used as follows:
*ptr = 7;
will copy 7 to the address pointed to by ptr. Thus if ptr "points to" (contains the address of) k,
the above statement will set the value of k to 7. That is, when we use the '*' this way we are
referring to the value of that which ptr is pointing to, not the value of the pointer itself. Similarly,
we could write:
printf("%d\n",*ptr);
to print to the screen the integer value stored at the address pointed to by ptr;.
92
(AMITY CENTER FOR e-LEARNING)
One way to see how all this stuff fits together would be to run the following program and then
review the code and the output carefully.
#include <stdio.h>
int j, k;
int *ptr;
int main(void)
{
j = 1;
k = 2;
ptr = &k;
printf("\n");
printf("j has the value %d and is stored at %p\n", j, (void *)&j);
printf("k has the value %d and is stored at %p\n", k, (void *)&k);
printf("ptr has the value %p and is stored at %p\n", ptr, (void *)&ptr);
printf("The value of the integer pointed to by ptr is %d\n", *ptr);
return 0;
}
7.2 Pointer types and Arrays
Okay, let's move on. Let us consider why we need to identify the type of variable that a pointer
points to, as in:
int *ptr;
One reason for doing this is so that later, once ptr "points to" something, if we write:
*ptr = 2;
the compiler will know how many bytes to copy into that memory location pointed to by ptr. If
ptr was declared as pointing to an integer, 4 bytes would be copied. Similarly for floats and
doubles the appropriate number will be copied. But, defining the type that the pointer points to
permits a number of other interesting ways a compiler can interpret code. For example, consider
a block in memory consisting if ten integers in a row. That is, 40 bytes of memory are set aside
93
(AMITY CENTER FOR e-LEARNING)
to hold 10 integers. Now, let's say we point our integer pointer ptr at the first of these integers.
Furthermore lets say that integer is located at memory location 100 (decimal). What happens
when we write:
ptr + 1;
Because the compiler "knows" this is a pointer (i.e. its value is an address) and that it points to an
integer (its current address, 100, is the address of an integer), it adds 4 to ptr instead of 1, so the
pointer "points to" the next integer, at memory location 104. Similarly, were the ptr declared as
a pointer to a short, it would add 2 to it instead of 1. The same goes for other data types such as
floats, doubles, or even user defined data types such as structures. This is obviously not the same
kind of "addition" that we normally think of. In C it is referred to as addition using "pointer
arithmetic", a term which we will come back to later.
Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1 (though the point in the program
when ptr is incremented may be different), incrementing a pointer using the unary ++ operator,
either pre- or post-, increments the address it stores by the amount sizeof(type) where "type" is
the type of the object pointed to. (i.e. 4 for an integer). Since a block of 10 integers located
contiguously in memory is, by definition, an array of integers, this brings up an interesting
relationship between arrays and pointers. Consider the following:
int my_array[] = {1,23,17,4,-5,100};
Here we have an array containing 6 integers. We refer to each of these integers by means of a
subscript to my_array, i.e. using my_array[0] through my_array[5]. But, we could alternatively
access them via a pointer as follows:
int *ptr;
ptr = &my_array[0];
/* point our pointer at the first integer in our array */
And then we could print out our array either using the array notation or by dereferencing our
pointer. The following code illustrates this:
94
(AMITY CENTER FOR e-LEARNING)
#include <stdio.h>
int my_array[] = {1,23,17,4,-5,100};
int *ptr;
int main(void)
{
int i;
ptr = &my_array[0];
/* point our pointer to the first element of the array */
printf("\n\n");
for (i = 0; i < 6; i++)
{
printf("my_array[%d] = %d ",i,my_array[i]); /*<-- A */
printf("ptr + %d = %d\n",i, *(ptr + i));
/*<-- B */
}
return 0;
}
Compile and run the above program and carefully note lines A and B and that the program prints
out the same values in either case. Also observe how we dereferenced our pointer in line B, i.e.
we first added i to it and then dereferenced the new pointer. Change line B to read:
printf("ptr + %d = %d\n",i, *ptr++);
and run it again... then change it to:
printf("ptr + %d = %d\n",i, *(++ptr));
and try once more. Each time try and predict the outcome and carefully look at the actual
outcome. In C, the standard states that wherever we might use &var_name[0] we can replace
that with var_name, thus in our code where we wrote:
ptr = &my_array[0];
we can write:
95
(AMITY CENTER FOR e-LEARNING)
ptr = my_array;
to achieve the same result.
This leads many texts to state that the name of an array is a pointer. I prefer to mentally think
"the name of the array is the address of first element in the array". Many beginners (including
myself when I was learning) have a tendency to become confused by thinking of it as a pointer.
For example, while we can write
ptr = my_array;
we cannot write
my_array = ptr;
The reason is that while ptr is a variable, my_array is a constant. That is, the location at which
the first element of my_array will be stored cannot be changed once my_array[] has been
declared. Earlier when discussing the term "lvalue" I cited K&R-2 where it stated:
"An object is a named region of storage; an lvalue is an expression referring to an object". This
raises an interesting problem. Since my_array is a named region of storage, why is my_array in
the above assignment statement not an lvalue? To resolve this problem, some refer to my_array
as an "unmodifiable lvalue". Modify the example program above by changing
ptr = &my_array[0];
to
ptr = my_array;
and run it again to verify the results are identical.
Now, let's delve a little further into the difference between the names ptr and my_array as used
above. Some writers will refer to an array's name as a constant pointer. What do we mean by
that? Well, to understand the term "constant" in this sense, let's go back to our definition of the
term "variable". When we declare a variable we set aside a spot in memory to hold the value of
the appropriate type. Once that is done the name of the variable can be interpreted in one of two
ways. When used on the left side of the assignment operator, the compiler interprets it as the
96
(AMITY CENTER FOR e-LEARNING)
memory location to which to move that value resulting from evaluation of the right side of the
assignment operator. But, when used on the right side of the assignment operator, the name of a
variable is interpreted to mean the contents stored at that memory address set aside to hold the
value of that variable. With that in mind, let's now consider the simplest of constants, as in:
int i, k;
i = 2;
Here, while i is a variable and then occupies space in the data portion of memory, 2 is a constant
and, as such, instead of setting aside memory in the data segment, it is imbedded directly in the
code segment of memory. That is, while writing something like k = i; tells the compiler to create
code which at run time will look at memory location &i to determine the value to be moved to k,
code created by i = 2; simply puts the 2 in the code and there is no referencing of the data
segment. That is, both k and i are objects, but 2 is not an object. Similarly, in the above, since
my_array is a constant, once the compiler establishes where the array itself is to be stored, it
"knows" the address of my_array[0] and on seeing:
ptr = my_array;
it simply uses this address as a constant in the code segment and there is no referencing of the
data segment beyond that.
7.3 Pointers and Strings
The study of strings is useful to further tie in the relationship between pointers and arrays. It also
makes it easy to illustrate how some of the standard C string functions can be implemented.
Finally it illustrates how and when pointers can and should be passed to functions. In C, strings
are arrays of characters. This is not necessarily true in other languages. In BASIC, Pascal,
Fortran and various other languages, a string has its own data type. But in C it does not. In C a
string is an array of characters terminated with a binary zero character (written as '\0'). To start
off our discussion we will write some code which, while preferred for illustrative purposes, you
would probably never write in an actual program. Consider, for example:
97
(AMITY CENTER FOR e-LEARNING)
char my_string[40];
my_string[0] = 'T';
my_string[1] = 'e';
my_string[2] = 'd':
my_string[3] = '\0';
While one would never build a string like this, the end result is a string in that it is an array of
characters terminated with a nul character. By definition, in C, a string is an array of
characters terminated with the nul character. Be aware that "nul" is not the same as "NULL".
The nul refers to a zero as defined by the escape sequence '\0'. That is it occupies one byte of
memory. NULL, on the other hand, is the name of the macro used to initialize null pointers.
NULL is #defined in a header file in your C compiler, nul may not be #defined at all. Since
writing the above code would be very time consuming, C permits two alternate ways of
achieving the same thing. First, one might write:
char my_string[40] = {'T', 'e', 'd', '\0',};
But this also takes more typing than is convenient. So, C permits:
char my_string[40] = "Ted";
When the double quotes are used, instead of the single quotes as was done in the previous
#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void)
{
char *pA;
/* a pointer to type character */
char *pB;
/* another pointer to type character */
puts(strA); /* show string A */
pA = strA;
/* point pA at string A */
puts(pA);
/* show what pA is pointing to */
98
(AMITY CENTER FOR e-LEARNING)
pB = strB;
putchar('\n');
/* point pB at string B */
/* move down one line on the screen */
while(*pA != '\0') /* line A (see text) */
{
*pB++ = *pA++; /* line B (see text) */
}
*pB = '\0';
/* line C (see text) */
puts(strB);
/* show strB on screen */
return 0;
}
In the above we start out by defining two character arrays of 80 characters each. Since these are
globally defined, they are initialized to all '\0's first. Then, strA has the first 42 characters
initialized to the string in quotes. Now, moving into the code, we declare two character pointers
and show the string on the screen. We then "point" the pointer pA at strA. That is, by means of
the assignment statement we copy the address of strA[0] into our variable pA. We now use
puts() to show that which is pointed to by pA on the screen. Consider here that the function
prototype for puts() is:
int puts(const char *s);
For the moment, ignore the const. The parameter passed to puts() is a pointer, that is the value
of a pointer (since all parameters in C are passed by value), and the value of a pointer is the
address to which it points, or, simply, an address. Thus when we write puts(strA); as we have
seen, we are passing the address of strA[0]. Similarly, when we write puts(pA); we are passing
the same address, since we have set pA = strA; Given that, follow the code down to the while()
statement on line A. Line A states: While the character pointed to by pA (i.e. *pA) is not a nul
character (i.e. the terminating '\0'), do the following:
Line B states: copy the character pointed to by pA to the space pointed to by pB, then increment
pA so it points to the next character and pB so it points to the next space.
99
(AMITY CENTER FOR e-LEARNING)
When we have copied the last character, pA now points to the terminating nul character and the
loop ends. However, we have not copied the nul character. And, by definition a string in C must
be nul terminated. So, we add the nul character with line C.
It is very educational to run this program with your debugger while watching strA, strB, pA and
pB and single stepping through the program. It is even more educational if instead of simply
defining strB[] as has been done above, initialize it also with something like:
strB[80] = "12345678901234567890123456789012345678901234567890"
where the number of digits used is greater than the length of strA and then repeat the single
stepping procedure while watching the above variables. Give these things a try! Getting back to
the prototype for puts() for a moment, the "const" used as a parameter modifier informs the user
that the function will not modify the string pointed to by s, i.e. it will treat that string as a
constant. Of course, what the above program illustrates is a simple way of copying a string. After
playing with the above until you have a good understanding of what is happening, we can
proceed to creating our own replacement for the standard strcpy() that comes with C. It might
look like:
char *my_strcpy(char *destination, char *source)
{
char *p = destination;
while (*source != '\0')
{
*p++ = *source++;
}
*p = '\0';
return destination;
}
100
(AMITY CENTER FOR e-LEARNING)
In this case, I have followed the practice used in the standard routine of returning a pointer to the
destination. Again, the function is designed to accept the values of two character pointers, i.e.
addresses, and thus in the previous program we could write:
int main(void)
{
my_strcpy(strB, strA);
puts(strB);
}
I have deviated slightly from the form used in standard C which would have the prototype:
char *my_strcpy(char *destination, const char *source);
Here the "const" modifier is used to assure the user that the function will not modify the contents
pointed to by the source pointer. You can prove this by modifying the function above, and its
prototype, to include the "const" modifier as shown. Then, within the function you can add a
statement which attempts to change the contents of that which is pointed to by source, such as:
*source = 'X';
which would normally change the first character of the string to an X. The const modifier should
cause your compiler to catch this as an error. Try it and see.
Now, let's consider some of the things the above examples have shown us. First off, consider the
fact that *ptr++ is to be interpreted as returning the value pointed to by ptr and then
incrementing the pointer value. This has to do with the precedence of the operators. Were we to
write (*ptr)++ we would increment, not the pointer, but that which the pointer points to! i.e. if
used on the first character of the above example string the 'T' would be incremented to a 'U'. You
can write some simple example code to illustrate this. Recall again that a string is nothing more
than an array of characters, with the last character being a '\0'. What we have done above is deal
with copying an array. It happens to be an array of characters but the technique could be applied
to an array of integers, doubles, etc. In those cases, however, we would not be dealing with
strings and hence the end of the array would not be marked with a special value like the nul
101
(AMITY CENTER FOR e-LEARNING)
character. We could implement a version that relied on a special value to identify the end. For
example, we could copy an array of positive integers by marking the end with a negative integer.
On the other hand, it is more usual that when we write a function to copy an array of items other
than strings we pass the function the number of items to be copied as well as the address of the
array, e.g. something like the following prototype might indicate:
void int_copy(int *ptrA, int *ptrB, int nbr);
where nbr is the number of integers to be copied. You might want to play with this idea and
create an array of integers and see if you can write the function int_copy() and make it work.
This permits using functions to manipulate large arrays. For example, if we have an array of
5000 integers that we want to manipulate with a function, we need only pass to that function the
address of the array (and any auxiliary information such as nbr above, depending on what we are
doing). The array itself does not get passed, i.e. the whole array is not copied and put on the
stack before calling the function, only its address is sent.
This is different from passing, say an integer, to a function. When we pass an integer we make a
copy of the integer, i.e. get its value and put it on the stack. Within the function any manipulation
of the value passed can in no way effect the original integer. But, with arrays and pointers we can
pass the address of the variable and hence manipulate the values of the original variables.
7.4 Pointers to Arrays
Pointers, of course, can be "pointed at" any type of data object, including arrays. While that was
evident when we discussed program 3.1, it is important to expand on how we do this when it
comes to multi-dimensional arrays. To review, that given an array of integers we could point an
integer pointer at that array using:
int *ptr;
ptr = &my_array[0];
/* point our pointer at the first integer in our array */
As we stated there, the type of the pointer variable must match the type of the first element of the
array. In addition, we can use a pointer as a formal parameter of a function which is designed to
manipulate an array. e.g.
102
(AMITY CENTER FOR e-LEARNING)
Given:
int array[3] = {1, 5, 7};
void a_func(int *p);
Some programmers might prefer to write the function prototype as:
void a_func(int p[]);
which would tend to inform others who might use this function that the function is designed to
manipulate the elements of an array. Of course, in either case, what actually gets passed is the
value of a pointer to the first element of the array, independent of which notation is used in the
function prototype or definition. Note that if the array notation is used, there is no need to pass
the actual dimension of the array since we are not passing the whole array, only the address to
the first element.
We now turn to the problem of the 2 dimensional array. As stated in the last chapter, C interprets
a 2 dimensional array as an array of one dimensional arrays. That being the case, the first
element of a 2 dimensional array of integers is a one dimensional array of integers. And a pointer
to a two dimensional array of integers must be a pointer to that data type. One way of
accomplishing this is through the use of the keyword "typedef". typedef assigns a new name to a
specified data type. For example:
typedef unsigned char byte;
causes the name byte to mean type unsigned char. Hence
byte b[10]; would be an array of unsigned characters.
Note that in the typedef declaration, the word byte has replaced that which would normally be
the name of our unsigned char. That is, the rule for using typedef is that the new name for the
data type is the name used in the definition of the data type. Thus in:
typedef int Array[10];
Array becomes a data type for an array of 10 integers. i.e. Array my_arr; declares my_arr as an
array of 10 integers and Array arr2d[5]; makes arr2d an array of 5 arrays of 10 integers each.
Also note that Array *p1d; makes p1d a pointer to an array of 10 integers. Because *p1d points
to the same type as arr2d, assigning the address of the two dimensional array arr2d to p1d, the
103
(AMITY CENTER FOR e-LEARNING)
pointer to a one dimensional array of 10 integers is acceptable. i.e. p1d = &arr2d[0]; or p1d =
arr2d; are both correct.
Since the data type we use for our pointer is an array of 10 integers we would expect that
incrementing p1d by 1 would change its value by 10*sizeof(int), which it does. That is,
sizeof(*p1d) is 20. You can prove this to yourself by writing and running a simple short
program.
Now, while using typedef makes things clearer for the reader and easier on the programmer, it is
not really necessary. What we need is a way of declaring a pointer like p1d without the need of
the typedef keyword. It turns out that this can be done and that
int (*p1d)[10];
is the proper declaration, i.e. p1d here is a pointer to an array of 10 integers just as it was under
the declaration using the Array type. Note that this is different from
int *p1d[10];
which would make p1d the name of an array of 10 pointers to type int.
7.5 Dynamic Allocation of Memory
There are times when it is convenient to allocate memory at run time using malloc(), calloc(), or
other allocation functions. Using this approach permits postponing the decision on the size of the
memory block need to store an array, for example, until run time. Or it permits using a section of
memory for the storage of an array of integers at one point in time, and then when that memory
is no longer needed it can be freed up for other uses, such as the storage of an array of structures.
When memory is allocated, the allocating function (such as malloc(), calloc(), etc.) returns a pointer. The
type of this pointer depends on whether you are using an older K&R compiler or the newer ANSI type
compiler. With the older compiler the type of the returned pointer is char, with the ANSI compiler it is
void.
104
(AMITY CENTER FOR e-LEARNING)
If you are using an older compiler, and you want to allocate memory for an array of integers you
will have to cast the char pointer returned to an integer pointer. For example, to allocate space
for 10 integers we might write:
int *iptr;
iptr = (int *)malloc(10 * sizeof(int));
if (iptr == NULL)
{ .. ERROR ROUTINE GOES HERE .. }
If you are using an ANSI compliant compiler, malloc() returns a void pointer and since a void
pointer can be assigned to a pointer variable of any object type, the (int *) cast shown above is
not needed. The array dimension can be determined at run time and is not needed at compile
time. That is, the 10 above could be a variable read in from a data file or keyboard, or calculated
based on some need, at run time. Because of the equivalence between array and pointer notation,
once iptr has been assigned as above, one can use the array notation. For example, one could
write:
int k;
for (k = 0; k < 10; k++)
iptr[k] = 2;
to set the values of all elements to 2.
Even with a reasonably good understanding of pointers and arrays, one place the newcomer to C
is likely to stumble at first is in the dynamic allocation of multi-dimensional arrays. In general,
we would like to be able to access elements of such arrays using array notation, not pointer
notation, wherever possible. Depending on the application we may or may not know both
dimensions at compile time. This leads to a variety of ways to go about our task. As we have
seen, when dynamically allocating a one dimensional array its dimension can be determined at
run time. Now, when using dynamic allocation of higher order arrays, we never need to know the
first dimension at compile time. Whether we need to know the higher dimensions depends on
how we go about writing the code. Here I will discuss various methods of dynamically allocating
room for 2 dimensional arrays of integers.
105
(AMITY CENTER FOR e-LEARNING)
Chapter 8
Structures and Union
Contents:
8.1 What is structure?
8.2 Functions and Structures
8.2.1
Passing structure to elements to functions
8.2.2
Passing entire function to functions
8.2.3
Arrays of structure
8.3 Union
8.4 More on structures and Union
8.1 What is a Structure?
Arrays are used to store large set of data and manipulate them but the disadvantage is that all the
elements stored in an array are to be of the same data type. If we need to use a collection of different
data type items it is not possible using an array. When we require using a collection of different data
items of different data types we can use a structure. Structure is a method of packing data of different
types. A structure is a convenient method of handling a group of related data items of different data
types. We can summarize structure as
Structure is a method of packing the data of different types.
When we require using a collection of different data items of different data types in that
situation we can use a structure.
A structure is used as a method of handling a group of related data items of different data types.
Syntax of Using Structure
structure definition:
general format:
struct tag_name
{
106
(AMITY CENTER FOR e-LEARNING)
data type member1;
data type member2;
}
Example of Using Structure:
struct lib_books
{
char title[20];
char author[15];
int pages;
float price;
};
To holds the details of four fields namely title, author pages and price,the keyword struct
declares a structure. These are the members of the structures. Each member may belong to same
or different data type. The tag name can be used to define the objects that have the tag names
structure. The structure we just declared is not a variable by itself but a template for the structure.
We can declare the structure variables using the tag name any where in the program. For
example the statement, struct lib_books book1, book2, book3; declares the book1, book2, book3
as variables of type struct lib_books each declaration has four elements of the structure
lib_books. The complete structure declaration might look like this
The complete structure declaration might look like this
struct lib_books
{
char title[20];
char author[15];
int pages;
float price;
};struct lib_books, book1, book2, book3;
8.2 Functions and structures
107
(AMITY CENTER FOR e-LEARNING)
We can pass structures as arguments to functions. Unlike array names however, which always
point to the start of the array, structure names are not pointers. As a result, when we change
structure parameter inside a function, we don‘t effect its corresponding argument.
8.4.1
Passing structure to elements to functions
A structure may be passed into a function as individual member or a separate variable. A
program example to display the contents of a structure passing the individual elements to a
function is shown below.
# include < stdio.h >
void main()
{
int emp_id;
char name[25];
char department[10];
float salary;
};
static struct emp1={125,‖sampath‖,‖operator‖,7500.00};
/* pass only emp_id and name to display function*/
display(emp1.emp_id,emp1.name);
}
/* function to display structure variables*/
display(e_no,e_name)
int e_no,e_name;
{
printf(―%d%s‖,e_no,e_name);
in the declaration of structure type, emp_id and name have been declared as integer and
character array. When we call the function display() using display(emp1.emp_id,emp1.name);
we are sending the emp_id and name to function display(0); it can be immediately realized that
108
(AMITY CENTER FOR e-LEARNING)
to pass individual elements would become more tedious as the number of structure elements go
on increasing a better way would be to pass the entire structure variable at a time.
8.2.2 Passing entire function to functions
In case of structures having to having numerous structure elements passing these individual
elements would be a tedious task. In such cases we may pass whole structure to a function as
shown below:
# include stdio.h>
{
int emp_id;
char name[25];
char department[10];
float salary;
};
void main()
{
static struct employee emp1=
{
12,
―sadanand‖,
―computer‖,
7500.00
};
/*sending entire employee structure*/
display(emp1);
}
/*function to pass entire structure variable*/
109
(AMITY CENTER FOR e-LEARNING)
display(empf)
struct employee empf
{
printf(―%d%s,%s,%f‖, empf.empid,empf.name,empf.department,empf.salary);
}
8.2.3 Arrays of structure
It is possible to define a array of structures for example if we are maintaining information of all
the students in the college and if 100 students are studying in the college. We need to use an
array than single variables. We can define an array of structures as shown in the following
example:
structure information
{
int id_no;
char name[20];
char address[20];
char combination[3];
int age;
}
student[100];
An array of structures can be assigned initial values just as any other array can. Remember that
each element is a structure that must be assigned corresponding initial values as illustrated
below.
#include< stdio.h >
{
struct info
{
int id_no;
110
(AMITY CENTER FOR e-LEARNING)
char name[20];
char address[20];
char combination[3];
int age;
}
struct info std[100];
int I,n;
printf(―Enter the number of students‖);
scanf(―%d‖,&n);
printf(― Enter Id_no,name address combination age\m‖);
for(I=0;I < n;I++)
scanf(%d%s%s%s%d‖,&std[I].id_no,std[I].name,std[I].address,std[I].combination,&std[I].age);
printf(―\n Student information‖);
for (I=0;I< n;I++)
printf(―%d%s%s%s%d\n‖,
‖,std[I].id_no,std[I].name,std[I].address,std[I].combination,std[I].age);
}
8.2.4 Structure within a structure
A structure may be defined as a member of another structure. In such structures the declaration
of the embedded structure must appear before the declarations of other structures.
struct date
{
int day;
int month;
int year;
};
struct student
{
111
(AMITY CENTER FOR e-LEARNING)
int id_no;
char name[20];
char address[20];
char combination[3];
int age;
structure date def;
structure date doa;
}oldstudent, newstudent;
the sturucture student constains another structure date as its one of its members.
Important points about a structure
1)A structure allocates the total size of all elements in it.
2) Members inside a structure are always stored in separate memory locations throughout the
lifetime and scope of the entire structure. Manipulations of one member will not affect the values
of any of the others in any way unless they are operated on in code to do so.
8.3 Union
However the members that we compose a union all share the same storage area within the
computers memory where as each member within a structure is assigned its own unique storage
area. Thus unions are used to observe memory. They are useful for the application involving
multiple members. Where values need not be assigned to all the members at any time. Unions
like structure contain members whose individual data types may differ from one another also.
Like structures union can be declared using the keyword union as follows:
union item
{
int m;
float p;
char c;
}
code
112
(AMITY CENTER FOR e-LEARNING)
The notation for accessing a union member that is nested inside a structure remains the same as
for the nested structure.In effect,a union creates a storage location that can be used by one of its
members at a time. When a different number is assigned to a new value the new value
supercedes the previous members value. Unions may be used in all the places where a structure
is allowed.
Important points about a union:
1) A union only allocates as much memory as its largest element (member) requires.
2) The union will store one and only one actual value for one element at a time. If another
element is stored before the first is retrieved, the first stored value is lost. This presents the
programmer with extra responsibility when using a union to determine that the logic will
properly store all values used in the union through a correct lifetime and scope.
8.4 More on Structures and Union
The difference between structure and union:
1. union allocates the memory equal to the maximum memory required by the member of the
union but structure allocates the memory equal to the total memory required by the members.
2. In union, one block is used by all the member of the union but in case of structure, each
member have their own memory space
Similarities between structure and union:
1) They both can accept the dot (.) operator to address a member from the object name, as
struct.member or union.member.
2) They both use brace delimited declarations to create the template for the data object. Both
accept tagname and name as well as explicit initialization as options.
3) They both can have their size correctly determined as maximum size in bytes by use of the
sizeof() operator.
An example program is shown below. I hope it helps you understand.
#include <stdio.h>
#include <stdlib.h>
/* Declares union */
113
(AMITY CENTER FOR e-LEARNING)
union one{
char one;
int two;
float three;
};
/* Declares structure */
struct two{
char one;
int two;
float three;
};
int main(void)
{
/* Uses tag names to create structure S and union U. */
struct two S;
union one U;
/* Outputs object sizes to screen. */
printf("%d is the size of S, as structure.\n", sizeof(S));
printf("%d is the size of U, as union.\n\n", sizeof(U));
/* Loads values into S and U, as below. */
S.one = 'A';
S.two = 3645;
S.three = 678.32;
U.one = 'A';
U.two = 3645;
U.three = 678.32;
114
(AMITY CENTER FOR e-LEARNING)
Chapter 9
Data Files
Contents:
9.1 Introduction
9.2 File Types
9.2.1 Text streams
9.2.2 Binary streams
9.3 Stdio.h header file
9.4 Oprations on file
9.4.1 Opening named files
9.4.2 Reading from a stream using fgetc
9.4.3 Writing to a stream using fputc
9.4.4 Closing files
9.1 Introduction
One of the reasons that has prevented many programming languages from becoming widely used
for ‗real programming‘ is their poor support for I/O, a subject which has never seemed to excite
language designers. C has avoided this problem, oddly enough, by having no I/O at all! The C
language approach has always been to do I/O using library functions, which ensures that system
designers can provide tailored I/O instead of being forced to change the language itself.As C has
evolved, a library package known as the ‗Standard I/O Library‘ or stdio, has evolved with it and
has proved to be both flexible and portable. This package has now become part of the Standard
We frequently use files for storing information which can be processed by our programs. In order to
store information permanently and retrieve it we need to use files Files are not only used for data. Our
programs are also stored in files.The editor which you use to enter your program and save it, simply
manipulate files for you.
115
(AMITY CENTER FOR e-LEARNING)
9.2 File Types
There are two types of file, text files and binary files, which, within a program, are manipulated
as text streams and binary streams once they have been opened for I/O. The stdio package does
not permit operations on the contents of files ‗directly‘, but only by viewing them as streams.
9.2.1 Text streams
The Standard specifies what is meant by the term text stream, which essentially considers a file
to contain lines of text. A line is a sequence of zero or more characters terminated by a newline
character. It is quite possible that the actual representation of lines in the external environment is
different from this and there may be transformations of the data stream on the way in and out of
the program; a common requirement is to translate the ‗\n‘ line-terminator into the sequence
‗\r\n‘ on output, and do the reverse on input. Other translations may also be necessary.
Data read in from a text stream is guaranteed to compare equal to the data that was earlier written
out to the file if the data consists only of complete lines of printable characters and the control
characters horizontal-tab and newline, no newline character is immediately preceded by space
characters and the last character is a newline.It is guaranteed that, if the last character written to a
text file is a newline, it will read back as the same. It is implementation defined whether the last
line written to a text file must terminate with a newline character; this is because on some
implementations text files and binary files are the same.
Some implementations may strip the leading space from lines consisting only of a space
followed by a newline, or strip trailing spaces at the end of a line! An implementation must
support text files with lines containing at least 254 characters, including the terminating newline.
Opening a text stream in update mode may result in a binary stream in some
implementations.Writing on a text stream may cause some implementations to truncate the file at
that point—any data beyond the last byte of the current write being discarded.
9.2.2. Binary streams
A binary stream is a sequence of characters that can be used to record a program's internal data,
such as the contents of structures or arrays in binary form. Data read in from a binary stream will
always compare equal to data written out earlier to the same stream, under the same
implementation. In some circumstances, an implementation-defined number of NUL characters
may be appended to a binary stream.The contents of binary files are exceedingly machine
specific, and not, in general, portable.
116
(AMITY CENTER FOR e-LEARNING)
9.3 The stdio.h header file
To provide support for streams of the various kinds, a number of functions and macros exist. The
<stdio.h> header file contains the various declarations necessary for the functions, together with
the following macro and type declarations:
FILE
The type of an object used to contain stream control information. Users of stdio never
need to know the contents of these objects, but simply manipulate pointers to them. It is
not safe to copy these objects within the program; sometimes their addresses may be
‗magic‘.
fpos_t
A type of object that can be used to record unique values of a stream's file position
indicator. .
BUFSIZ
The size of the buffer used by the setbuf function. An integral constant expression whose
value is at least 256.
EOF
A negative integral constant expression, indicating the end-of-file condition on a stream
i.e. that there is no more input.
SEEK_CUR SEEK_END SEEK_SET
Integral constant expressions used to control the actions of fseek.
stdin stdout stderr
Predefined objects of type (FILE *) referring to the standard input, output and error
streams respectively. These streams are automatically open when a program starts
execution.
9.4 Operations on file
The primary difference between manipulating files and doing terminal I/O is that we must specify in our
programs which files we wish to use. As you know, you can have many files on your disk. If you wish
to use a file in your programs, then you must specify which file or files you wish to use.
Specifying the file you wish to use is referred to as opening the file. When you open a file you must
also specify what you wish to do with it i.e. Read from the file, Write to the file, or both. Because you
may use a number of different files in your program, you must specify when reading or writing which
file you wish to use. This is accomplished by using a variable called a file pointer.
Every file you open has its own file pointer variable. When you wish to write to a file you specify the
file by using its file pointer variable.You declare these file pointer variables as follows:
117
(AMITY CENTER FOR e-LEARNING)
FILE *fopen(), *fp1, *fp2, *fp3;
The variables fp1, fp2, fp3 are file pointers. You may use any name you wish.
The file <stdio.h> contains declarations for the Standard I/O library and should always be included at
the very beginning of C programs using files. Constants such as FILE, EOF and NULL are defined in
<stdio.h>.You should note that a file pointer is simply a variable like an integer or character. It does not
point to a file or the data in a file. It is simply used to indicate which file your I/O operation refers to. A
file number is used in the Basic language and a unit number is used in Fortran for the same purpose.
9.4.1 Opening named files
Named files are opened by a call to the fopen function, whose declaration is this:
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
The pathname argument is the name of the file to open, such as that returned from tmpnam, or
some program-specific filename.Files can be opened in a variety of modes, such as read mode
for reading data, write mode for writing data, and so on. Note that if you only want to write data
to a file, fopen will create the file if it does not already exist, or truncate it to zero length (losing
its previous contents) if it did exist The function fopen is one of the Standard Library functions
and returns a file pointer which you use to refer to the file you have opened e.g.
fp = fopen( ―prog.c‖, ―r‖) ;
The above statement opens a file called prog.c for reading and associates the file pointer fp with the
file. When we wish to access this file for I/O, we use the file pointer variable fp to refer to it. You can
have up to about 20 files open in your program - you need one file pointer for each file you intend to
use. If you attempt to read from an non-existent file, your program will crash!!
The fopen function was designed to cope with this eventuality. It checks if the file can be opened
appropriately. If the file cannot be opened, it returns a NULL pointer. Thus by checking the file
pointer returned by fopen, you can determine if the file was opened correctly and take appropriate
action e.g.
118
(AMITY CENTER FOR e-LEARNING)
fp = fopen (filename, ―r‖) ;
if ( fp == NULL)
{
printf(―Cannot open %s for reading \n‖, filename );
exit(1) ; /*Terminate program: Commit suicide !!*/
}
The above code fragment show how a program might check if a file could be opened appropriately.The
function exit() is a special function which terminates your program immediately. exit(0) mean that you
wish to indicate that your program terminated successfully whereas a nonzero value means that your
program is terminating due to an error condition.Alternatively, you could prompt the user to enter the
filename again, and try to open it again:
fp = fopen (fname, ―r‖) ;
while ( fp == NULL)
{
printf(―Cannot open %s for reading \n‖, fname );
printf(―\n\nEnter filename :‖ );
gets( fname );
fp = fopen (fname, ―r‖) ;
}
In this code fragment, we keep reading filenames from the user until a valid existing filename is
entered.
9.4.2 Reading from a stream using fgetc
The fgetc function is used to read a character from a stream.
119
(AMITY CENTER FOR e-LEARNING)
int fgetc(FILE *fp);
If successful, fgetc returns the next byte or character from the stream (depending on whether the
file is "binary" or "text", as discussed under fopen above). If unsuccessful, fgetc returns EOF.
(The specific type of error can be determined by calling ferror or feof with the file pointer.) The
standard macro getc, also defined in <stdio.h>, behaves in almost the same way as fgetc, except
that — being a macro — it may evaluate its arguments more than once.The standard function
getchar, also defined in <stdio.h>, takes no arguments, and is equivalent to getc(stdin).
9.4.3 Writing to a stream using fputc
The fputc function is used to write a character to a stream.
int fputc(int c, FILE *fp);
The routine getc(fp) is similar to getchar() and putc(c,fp) is similar to putchar(c). Thus the statement
c = getc(fp);
reads the next character from the file referenced by fp and the statement
putc(c,fp);
writes the character c into file referenced by fp.
/* file.c: Display contents of a file on screen */
#include <stdio.h>
void main()
{
FILE *fopen(), *fp;
int c ;
fp = fopen( ―prog.c‖, ―r‖ );
c = getc( fp ) ;
while ( c != EOF )
{
putchar( c );
c = getc ( fp );
120
(AMITY CENTER FOR e-LEARNING)
}
fclose( fp );
}
In this program, we open the file prog.c for reading. We then read a character from the file. This file
must exist for this program to work. If the file is empty, we are at the end, so getc returns EOF a special
value to indicate that the end of file has been reached. (Normally -1 is used for EOF) The while loop
simply keeps reading characters from the file and displaying them, until the end of the file is reached
9.4.4 Closing files
An open file is closed using fclose.
#include <stdio.h>
int fclose(FILE *stream);
Any unwritten data buffered for stream is flushed out and any unread data is thrown away. If a
buffer had been automatically allocated for the stream, it is freed. The file is then closed.Zero is
returned on success, EOF if any error occurs The function fclose is used to close the file i.e.
indicate that we are finished processing this file. We could reuse the file pointer fp by opening
another file.
/* Prompt user for filename and display file on screen */
#include <stdio.h>
void main()
{
FILE *fopen(), *fp;
int c ;
char filename[40] ;
121
(AMITY CENTER FOR e-LEARNING)
printf(―Enter file to be displayed: ―);
gets( filename ) ;
fp = fopen( filename, ―r‖);
c = getc( fp ) ;
while ( c != EOF )
{
putchar(c);
c = getc ( fp );
}
fclose( fp );
}
In this program, we pass the name of the file to be opened which is stored in the array called filename,
to the fopen function. In general, anywhere a string constant such as ―prog,c‖ can be used so can a
character array such as filename. (Note the reverse is not true).The above programs suffer a major
limitation. They do not check whether the files to be used exist or not.
Example usage
The following C program opens a binary file called myfile, reads five bytes from it, and then
closes the file.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
122
(AMITY CENTER FOR e-LEARNING)
char buffer[5] = {0}; /* initialized to zeroes */
int i, rc;
FILE *fp = fopen("myfile", "rb");
if (fp == NULL) {
perror("Failed to open file \"myfile\"");
return EXIT_FAILURE;
}
for (i = 0; (rc = getc(fp)) != EOF && i < 5; buffer[i++] = rc)
;
fclose(fp);
if (i == 5) {
puts("The bytes read were...");
printf("%x %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
} else
fputs("There was an error reading the file.\n", stderr);
return EXIT_SUCCESS;
}
123
(AMITY CENTER FOR e-LEARNING)
BIBLIOGRAPHY
1. Programming in ANSI C, E Balagurusamy , TMH
2. Let Us C, Y Kanetkar , BPB
3. The C Programming Language, B.W. Kernigham & Ritchie, PHI
4. Programming in C , Gottrified, TMH
124