C Programming
Page 1
Chapter 1
Introduction
Page
2
The Genesis of C
Page 3
Language B was in turn influenced by BCPL (Basic
Control Programming Language) of Martin Richards. Its
name most probably represents a contraction of BCPL.
In 1971, Dennis Ritchie made extensions to language B,
and also rewrote its compiler to generate PDP-11
machine instructions.
Thus, the transition from B to C was contemporaneous
with the creation of a compiler capable of producing
programs fast and small enough to compete with
assembly language.
The Genesis of C
Page 4
By early 1973, the essentials of modern C were
complete at AT & T Bell Lab.
The language and compiler were strong enough to
permit the rewriting of the Unix kernel for the PDP-
11 in C during the summer of 1973.
Because the language could not live in isolation, the
prototypes for the modern libraries of the C
language were developed.
Nature of the C Language
Page 5
From its evolution, it was natural for C to exhibit the
powerful low-level features of second generation
languages like pointers, bit manipulation etc.
It also supports conditional constructs, loop constructs,
a rich list of operators, and a variety of data types
that is so typical of any third generation language.
The combined features of second and third generation
language make C a very powerful and a flexible
language.
Nature of the C Language
Page 6
These features of C make it possible to use the
language for systems programming like
development of compilers, interpreters, operating
systems, graphics and general utilities.
It is also ideal for a host of applications in the
commercial environment.
Much of the Unix operating system has been
written in the C language.
Block Structured Nature of C
Page 7
The basic unit of code in C is written as a C function, that
can in turn be used as building blocks to write more C
functions, thus facilitating modular programming.
C provides all features of structured programming such
as writing programs as functions, functions returning
values, functions defining its own local variables etc.
C language offers only a handful of functions that form
the core of the language; rest of the functions, available
as part of the language libraries, are developed using
the core functions, thus expanding the scope of the
language.
Block Structured Nature of C
Page 8
This promotes functionally cohesive code and
therefore, facilitates reusability.
Thus standalone functions can be written as part of
a library that can be deployed in other
applications.
The C Language: Features
Page 9
Pointers: In addition to referencing a memory location
through a variable name, the C language allows
reference to a memory location by its internal address,
or byte number called a pointer.
Memory Allocation: Generally, in most programming
languages, memory is assigned to a variable name at
the time of definition. C allows dynamic allocation of
memory; i.e., a C program can request the operating
system to release memory for the use of the program
at the time of execution.
The C Language: Features
Page 10
Recursion: In C, a function can call itself. Such
functions are called recursive functions.
Bit Manipulation: C allows manipulation of data in
its lowest form of storage; i.e., it is possible to
manipulate the bits within a byte using bit shift
operators (right bit shift, left bit shift operators),
and the bitwise or |, bitwise exclusive or ^, and
the bitwise and & operator.
The C Language: Features
Page 11
Keywords: There are only 32 keywords, i.e., reserved
words that cannot be used as a variable declaration.
The C99 has introduced five additional keywords.
Library Functions: C is accompanied by a number of
library functions to carry out various commonly used
operations. These functions are not part of the
language, but are supplied as a part of C compiler.
Features which tend to be computer-dependent are
generally written as library functions.
The Hello World Program
Page 12
/* Comment statements */ /* The Hello World program */
Preprocessor directives # include <stdio.h>
main() main()
{ {
Program statements; printf(“Hello World\n”);
} }
C Program – Hello World!
Page 13
Striking features:
• C is case sensitive. All commands must be lower case
• Starting point of every C program is identified by
word main()
• Braces ({...}) are used to enclose a block of
statements to be treated as a unit
• Comments are enclosed in /* and */ delimiters
• End of each statement must be marked with a
semicolon ( ; )
C Program – Hello World!
Page 14
#include <stdio.h> statement is used to allow the
use of the printf statement to provide program
output. For each function built into the language, an
associated header file must be included.
void main() informs the computer as to where the
program actually starts. The parentheses that follow
the keyword main indicate that there are no
arguments supplied to this program
Braces signify the beginning and end segments of
the program displayed on the screen with the cursor
set to the beginning of the next line
Factored Code: An Example
Page 15
/* Comment statements */ /* A sample C program */
Preprocessor directives # include <stdio.h>
Function prototypes; int sum();
global variable declarations; int a=10, b=20;
main() main()
{ {
local variable declarations; int c;
Program statements; c = sum();
} printf(“%d+%d = %d \n”,a,b,c);
}
[Additional functions ….] int sum()
{
return(a+b);
}
The C Language Data Types
Page 16
In C, all variables must be declared before they are
used, usually at the beginning of a function, before any
executable statements.
A declaration announces the properties of variables; it
consists of a type name, and a list of variables.
The type of a variable actually determines the type of
operations that can be performed on a variable of that
type.
Fundamental Data Types
Page 17
Fundamental data types are data types implemented
at the lowest level, i.e., those which are used for actual
data representation in memory.
The fundamental data types are:
char – for characters and strings
int – for integers
float – for numbers with decimals
Since the fundamental data types are implemented at
the machine level, their storage requirement is machine-
dependent.
Fundamental Data Types
Page 18
The type int means that the variables listed are integers.
The range of both int and float depends on the machine
you are using; 16-bits ints, which lie between -32768 and
+32767, are common, as are 32-bit ints.
Floating point variables on the other hand, are those that
have a decimal part. A float number is typically a 32-bit
quantity, with at least six significant digits, and magnitude
between about 10-38 and 1038.
Character variables, on the other hand, occupy a single
byte of storage.
Variations on Fundamental Data
Types
Page 19
C provides several other data types besides int
and float, including:
short: short integer
long: long integer
double: double-precision floating point
The size of these variables is also machine-
dependent.
Defining Data
Page 20
The general format for defining a variable of a
data type is [data type] [variable name]
All data is normally defined at the beginning of a
function.
The definition:
char var;
defines a memory location of size one byte, of character
type, referred to as var.
Defining Data
Page 21
Data Definition Data Memory Size Value
Type Defined Assigned
char x, y char x 1 byte -
y 1 byte -
int m; int m 4 bytes 22
int a, m=22;
a 4 bytes -
float num float num 4 bytes -
float num1= 9.67 float num1 4 bytes 9.67
Page 21
Defining Data
Page 22
The definition:
char var; defines a variable location called var, which is
one byte in size, and can therefore hold only one
character.
To define a memory location that can store characters in
contiguous locations as in a name, and addressable by
one variable name, you would need to use the following
definition:
char name[10];
The above definition defines a memory location called
name containing 10 contiguous bytes for storing up to 9
valid characters.
Keywords in C Programming
Page 23
Keywords in C Programming
auto break case char
const continue default do
double else enum extern
float for goto if
int long register return
short signed sizeof static
struct switch typedef union
unsigned void volatile while
C Programming ver 2.0
The Standard C Environment
Page 24
The C environment assumes the keyboard to be the
standard input device referred to as stdin
The VDU is assumed to be the standard output
device referred to as stdout
The VDU also serves as the standard error device,
and is referred to as stderr
Input/Output Functions
Page 25
C supports Input/Output operations through functions
written in C, and that are part of the standard C
library along with other functions.
These input/output functions may be incorporated into
any program by a programmer.
Any input or output operation happens as a stream of
characters. The standard I/O functions are available
for character-based I/O, or for string-based I/O.
Buffered I/O
Page 26
The standard I/O functions are buffered, i.e., each
device has an associated buffer through which any
input or output operation takes place.
After an input operation from the standard input
device has occurred, care must be taken to clear the
standard input buffer. Otherwise, the previous
contents of the buffer may interfere with subsequent
input.
After an output operation, the buffer need not be
cleared since subsequent output data will flush the
previous buffer contents.
Character-Based I/O
Page 27
The function getch( ) is used to accept a character from
standard input. By default, it accepts characters from
the keyboard, and returns the character input from the
keyboard.
The function putch( ) displays the character on to
standard output. It takes one argument, namely, the
character to be output.
The function fflush( ) clears the buffer associated with
the particular device.
Character-Based I/O: An Example
Page 28
#include <conio.h>
#include <stdio.h>
main( )
{
char ch;
ch = getch( );
fflush(stdin);
putch(ch);
}
Character-Based I/O
Page 29
The macro getchar( ) by default accepts a
character from the keyboard. Hence, it does not
need any parameters.
The macro putchar( ) is used to display the
character on to standard output. It takes the
character to be output as an argument.
Character-Based I/O: An Example
Page 30
#include <stdio.h>
main( )
{
char ch;
ch = getchar( );
fflush(stdin);
putchar(ch);
}
String-Based I/O
Page 31
The function gets( ) accepts as a parameter, a string
variable, or a string literal (enclosed in double
quotes) from the keyboard.
The function puts( ) accepts a string variable, or a
string literal to be displayed to standard output.
After displaying the output, the puts( ) function
causes the cursor to be positioned at the beginning
of the next line.
String-Based I/O
Page 32
#include <stdio.h>
#include <conio.h>
main( )
{
char str[11];
puts("Enter a string of maximum 10 characters");
gets(str);
fflush(stdin);
puts(str);
}
Conditional Constructs
Page 33
The C language provides different conditional
constructs for evaluating a condition in a program.
The relational operators required for evaluating the
conditions are similar to those in other third-
generation languages, though the symbols are
different.
Conditional Constructs
Page 34
Relational Operators:
Operator Meaning
== Equal to
!= Not equal to
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
Page 34
The if-else Construct
Page 35
The following function checks whether the character start
entered by the user is a star (*) character.
#include <stdio.h>
#include <conio.h> ch = ‘ ‘
main( )
{ Input ch
char ch;
ch = getchar( );
fflush(stdin); is ch = ‘*’ Print *
if (ch == '*')
puts ("You have entered the star character");
else Not a *
puts ("You have not entered the star character");
}
stop
The if-else Construct
Page 36
The earlier function can also be alternately written as:
#include <stdio.h>
#include <conio.h>
main( )
{
char ch;
ch = getchar( );
fflush(stdin);
if (ch == '*')
puts ("You have entered the star character");
else
puts ("You have not entered the star character");
}
Cascading if-else Construct
Page 37
The cascading if-else construct is also known as the
multiple if-else construct.
On the first condition evaluating to false, the else
part of the first condition consists of another if
statement that is evaluated for a condition.
If this condition evaluates to false, the else part of
this if condition consists of another if statement that
is evaluated for a condition, and so on.
Cascading if-else Construct
Page 38
#include <stdio.h>
main( )
{
char chr;
chr = getchar( );
if (chr == '0')
puts("You entered the number 0");
else
if (chr == '1')
puts("You entered the number 1");
else
if (chr == '2')
puts("You entered the number 2");
Cascading if-else Construct
Page 39
else if (chr == '3')
puts("You entered the number 3");
else if (chr == '4')
puts("You entered the number 4");
else if (chr == '5')
puts("You entered the number 5");
else if (chr == '6')
puts("You entered the number 6");
else if (chr == '7') no
puts("You entered the number 7"); ”
else if (chr == '8') yes
puts("You entered the number 8");
else if (chr == '9') no
”
puts("You entered the number 9");
else yes
puts("You did not enter a number");
} no
”
yes
Nested if Construct
Page 40
A nested if statement is
encountered if the statement to
be executed after a condition
evaluates to true is another if
statement.
Both the outer if statement and
no
the inner if statement have to ye
s ”
evaluate to true for the
ye
statement following the inner if s
no
condition to be executed.
Nested if Construct: Example
Page 41
The following program determines whether the character entered is an
uppercase or a lowercase alphabet.
#include<stdio.h>
main( )
{
char ch;
ch = getchar( );
fflush(stdin);
if (ch >= 'A')
if (ch <= 'Z')
puts ("Its an Uppercase alphabet");
else if (ch >= 'a')
if (ch <= 'z')
Nested if Construct: Example
Page 42
puts ("It’s a lowercase alphabet");
else
puts ("Input character > z");
else
puts ("Input character greater than Z but less than a");
else
puts ("Input character less than A");
}
Using Braces to Improve Readability
Page 43
#include< stdio.h>
main( )
{
char ch;
ch = getchar( );
fflush(stdin);
if (ch >= ‘A’)
{
if (ch <= ‘Z’)
puts (Its an Uppercase alphabet”);
else if (ch >= ‘a’)
{
if (inp <= ‘z’)
puts (“It’s a lowercase alphabet”);
else
puts (“Input character > z”);
}
Using Braces to Improve Readability
Page 44
else
puts (“Input character greater than Z but less than a”);
}
else
puts (“Input character less than A”);
}
The switch-case Conditional Construct
Page 45
The switch-case conditional construct is a more
structured way of testing for multiple conditions rather
than resorting to a cascading, or a multiple if statement.
A switch-case statement makes for better readability,
and hence makes code better understandable.
The switch-case statement is used in situations where
based on the value of a variable, certain actions need
to be taken.
The switch-case Conditional Construct
Page 46
switch ( ) specifies the variable name which has to be
compared with the specified cases. The variable may
be an integer, or a character variable.
The case statements specify values which may be
integer, or character constants. They also specify the
action to be taken if the value specified in the case
statement matches with the value of the switch
variable.
After the action for a particular case value is specified,
the break statement is used to bring control out of the
switch block of statements, and to the statement
immediately following the switch block.
The switch-case Conditional Construct
Page 47
If the break statement is not specified after the
statements associated with each case statement, then all
the statements associated with the other case
statements will be executed.
This will be in addition to the statements associated with
the case statement whose value evaluated to the value
of the variable specified in the switch statement.
This is because when one of the case statements is
evaluated as true, the action statements are executed
until a break statement sends control out of the switch
block of statements, or the switch block comes to an
end.
The switch-case Conditional Construct
Page 48
The default statement is optional, and is used for
handling situations where none of the case statements
are evaluated as true.
The case and default statements can occur in any order.
#include <stdio.h>
main( )
{
char chr;
chr = getchar( );
fflush (stdin);
The switch-case Conditional Construct
Page 49
switch ( chr)
{
case '0' : puts( "you entered 0");
break;
case '1' : puts( "you entered 1");
break;
case '2' : puts( "you entered 2");
break;
case '3' : puts( "you entered 3");
break;
case '4' : puts( "you entered 4");
break;
case '5' : puts( "you entered 5");
break;
case '6' : puts( "you entered 6");
break;
The switch-case Conditional Construct
Page 50
case '7‘ : puts( "you entered 7");
break;
case '8' : puts( "you entered 8");
break;
case '9' : puts( "you entered 9");
break;
default : puts ("You did not enter a number");
}
}
Iterative Constructs - The while Loop
Page 51
In a while loop, the loop condition is
written at the top followed by the
body of the loop. Evaluate Condition
false
Therefore, the loop condition is true
evaluated first, and if it is true, the
loop body is executed. Execute body of loop
After the execution of the loop
body, the condition in the while
statement is evaluated again. This
repeats until the condition becomes
false.
Iterative Constructs - The while Loop
Page 52
#include <stdio.h>
/* function to accept a string and display it 10 times */
main( )
{
int counter=0;
char message[10];
gets( message);
fflush( stdin);
while (counter <= 9)
{
puts( message);
putchar ('\r');
counter = counter + 1;
gets( message);
fflush (stdin); } }
Iterative Constructs – The do…while
loop
Page 53
In this loop construct, the body of the loop comes
first followed by the loop condition at the end.
Execute body of loop
Therefore, when this loop construct is used, the
body of the loop is guaranteed to execute at
least once.
false
The loop is entered into straightaway, and after Evaluate Condition
the first execution of the loop body, the loop
condition is evaluated. true
Subsequent executions of the loop body would
be subject to the loop condition evaluating to
true.
Iterative Constructs – The do…while
Page 54
loop
/* This is an example of a do-while loop */
#include <stdio.h>
main()
{
int i;
i = 0;
do {
printf("The value of i is now %d\n",i);
i = i + 1;
} while (i < 5);
}
Iterative Constructs – The for loop
Page 55
The for loop construct is by far the most powerful
and compact of all the loop constructs provided by
C.
This loop keeps all loop control statements on top of
the loop, thus making it visible to the programmer.
The for loop works well where the number of
iterations of the loop is known before the loop is
entered into.
Iterative Constructs – The for loop
Page 56
The header of the loop consists of three parts
separated by semicolons:
The first part is executed before the loop is entered.
This is usually the initialization of the loop variable.
The second is a test. The loop is terminated when this
test returns a false.
The third part is a statement to be run every time the
loop body is completed. This is usually an increment of
the loop counter.
Iterative Constructs – The for loop
Page 57
#include <stdio.h>
/* this function displays a message 10 times */
main( )
{
int i;
char message[10];
gets (message);
fflush(stdin);
for( i = 0; i <= 9; i = i + 1)
{
puts( message);
}
}
Control of Loop Execution
Page 58
A loop construct, whether while, or do-while, or a
for loop continues to iteratively execute until the
loop condition evaluates to false.
But there may be situations where it may be
necessary to exit from a loop even before the loop
condition is reevaluated after an iteration.
The break statement is used to exit early from all
loop constructs (while, do-while, and for).
Control of Loop Execution
Page 59
The continue statement used in a loop causes all
subsequent instructions in the loop body (coming after
the continue statement) to be skipped.
Control passes back to the top of the loop where the
loop condition is evaluated again.
In case of a continue statement in a for loop construct,
control passes to the reinitialization part of the loop,
after which the loop condition is evaluated again.
Summary
Page 60
In this session, you learnt to:
Describe the evolution of the C programming
language
Describe C as a second-generation as well as a
third-generation language
State the data types in C
Write simple C functions for input and output
Write C functions that employ conditional constructs
Write C functions that employ iterative constructs
Writing Compound Conditions Using Logical
Operators
Page 61
Operator Notation
NOT !
AND &&
OR ||
Page 61
The Logical AND Operator
Page 62
The result of a logical AND operation is true if both
operands are true.
It has the general form:
expression1 && expression2
Which evaluates to 1 (true) if both expression1 and
expression2 are 1 (true); otherwise evaluates to false
even if one of them evaluates to 0 (false).
The Logical AND Operator
Page 63
Some examples of valid AND expressions are:
a && b;
(a < b) && (c < d)
The Logical OR Operator
Page 64
The result of a logical OR operation is false only if
both the operands are false.
It has the general form:
expression1 || expresssion2
which evaluates to 1 (true) if either or both
expressions are 1, otherwise evaluates to 0 (false).
The Logical OR Operator
Page 65
Some examples of valid OR expressions:
a || b
(a < b) || (c < d)
Some examples of invalid OR expressions:
a || /* one operand missing */
a | | b /* space not allowed */
The Logical NOT Operator
Page 66
C also includes the operator ! that negates the value of
a logical expression; i.e., it causes an expression that is
originally true to become false, and vice-versa.
This operator is referred to as the logical negation, or
complement, or logical NOT.
It has the general form:
!expression
which evaluates to 1 (true) if the expression is 0,
otherwise evaluates to 0 (false)
The Logical NOT Operator
Page 67
For example, the expression !(k == 4) is true if the
value of k is not equal to 4, and false otherwise.
Some examples of valid ! expressions:
!a
!(x + 7)
Some examples of invalid ! expressions:
a! /* out of order */
Logical Expressions
Page 68
Values of Logical Expressions
a b a && b a || b
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 1
Page 68
Formatted I/O
Page 69
C provides standard functions for performing
formatted input and output. These functions
accept as parameters a format string and a list
of variables.
The format string consists of a specification for
each of the variables in terms of its data type,
called the conversion character, and width of
input or output.
Formatted Output
Page 70
The function printf( ) is used for formatted output to
standard output based on a format string.
The format string, along with the data to be output,
are the parameters to the printf( ) function.
The syntax of the printf( ) function is:
printf( “format string”, var1,var2…..)
Format string is a string containing the format
specification introduced by the character %, and ended
by a conversion character.
Formatted Output
Page 71
An example:
printf(“%c\n”, var);
The conversion characters and their meanings are:
Conversion character Meaning
d The data is converted to decimal
c The data is treated as a character
s The data is a string, and characters from
the string are printed until a null
character is reached, or until the
specified number of characters have
been exhausted
f The data is output as float or double
with a default precision of 6
Page 71
Formatted Output
Page 72
Between the % character and the conversion character, there may be:
A minus sign Implying left adjustment of data
A digit Implying the minimum width in which the data is to be output. If
the data has larger number of characters than the specified width,
the width occupied by the output is larger. If the data consists of
fewer characters than the specified width, it is padded to the right
(if minus sign is specified), or to the left (if no minus sign is
specified). If the digit is prefixed with a zero, the padding is done
with zeroes instead of blanks
A period Separates the width from the next digit.
A digit Specifying the precision, or the maximum number of characters
to be output
l To signify that the data item is a long integer, and not an integer.
Page 72
Formatted Output
Page 73
Format String Data Output
%2d 4 |4|
%2d 224 |224|
%03d 8 |008|
%-2d 4 |4 |
%5s Sherlock Holmes | Sherlock Holmes|
%15s Sherlock Holmes | Sherlock Holmes |
%-15s Sherlock Holmes | Sherlock Holmes |
%f 22.44 |22.440000|
Page 73
Data Conversion Using Format String
Page 74
A variable of a data type can be output as
another data type using the conversion character.
#include<stdio.h>
main( )
{
int number = 97;
printf("Value of num is %d\n", number);
printf("The Character equivalent of %d is %c\n", number, number);
}
Formatted Input
Page 75
The function scanf( ) is used for formatted input, and
provides many of the conversion facilities of printf( ).
The syntax of the function scanf( ) is:
scanf( format string, var1, var2….)
The scanf( ) function reads and converts characters
from standard input according to the format string, and
stores the input in memory locations specified by the
other arguments.
Formatted Input
Page 76
#include<stdio.h>
main( )
{
char name[10];
int age = 0;
char gender = ' ';
scanf ("%7s %c %2d", name, &gender, &age);
fflush(stdin);
printf( "% s %c %d", name, gender, age);
}
String Input Using scanf( )
Page 77
Remember that while accepting strings using scanf( ), a
space is considered as a string terminator. Hence,
scanf( ) cannot be used to accept strings with
embedded spaces.
#include<stdio.h>
main( )
{
char string[40];
printf("Enter a string of maximum 39 characters");
scanf("%s", string);
fflush(stdin);
printf("%s", string);
}
Unary Operators
Unary operators, as the name suggests, works on one
Page 78
operand or variable.
The ++ and the -- operators are examples of unary
operators.
When these operators prefix an operand, they are
referred to as prefix operators, and when they are
suffixed to an operand, they are referred to as postfix
operators.
Prefix and postfix operators, when used in an
expression, can have totally different results, and hence
it is necessary to know their workings.
Unary Operators
Page 79
Incrementing and decrementing by 1 is such a
ubiquitous operation that C offers the prefix and
postfix implementations of the ++ and the -- operators.
The expression i = i + 1; can also be written as i++.
When used as a standalone statement, writing i++, or
++i does not make any difference at all.
It is only when they are used as part of an
expression that the prefix and the postfix operators
can have totally different results.
Unary Operators
Page 80
Consider the following two statements:
total = i++;
total = ++i;
The first statement total = i++; is equivalent to:
total = i;
i++;
This is equivalent to assigning to total the current
value of i, and then incrementing i.
Unary Operators
Page 81
The second statement total = ++i; is equivalent to:
i = i + 1;
total = i;
This is equivalent to first incrementing the value of i, and
then assigning the incremented value of i to total.
The same principle holds true when working with the
unary -- operators.
Binary Operators
Page 82
Binary operators as the name suggests, works on
two operands.
The binary operators in C (as in other programming
languages) are:
The add ( + ) operator
The subtract ( - ) operator.
The multiply (* ) operator
The divide ( / ) operator
The modulo ( % ) operator
Binary Operators
Page 83
Examples of binary operators:
int x, y, z;
x = 27;
y = x % 5; /* y set to 2 */
z = x / 5 /* z set to 5 and not 5.4 */
Type Conversions
Page 84
When an operator has operands of different types,
they are converted to a common type according to
a small number of rules.
In general, the only automatic conversions are those
that convert a “narrower” operand into a “wider”
one without losing information, such as converting an
integer into floating point
Type Conversions
Page 85
Expressions that might lose information, like assigning a
longer integer type to a shorter, or a floating-point type to
an integer, may draw a warning, but they are not illegal.
A char is just a small integer, so chars may be freely used in
arithmetic expressions. This permits considerable flexibility in
certain kinds of character transformations.
Implicit arithmetic conversions work much as expected. In
general, if an operator like +, or * that takes two operands
(a binary operator) has operands of different types, the
“lower” type is promoted to the “higher” type before the
operation proceeds.
Type Conversion
Page 86
The following informal set of rules will suffice:
If either operand is long double, convert the other to long
double.
Otherwise, if either operand is double, convert the other to
double.
Otherwise, if either operand is float, convert the other to float.
Otherwise, convert char and short to int.
Then, if either operand is long, convert the other to long.
Conversions take place across assignments; the value of the
right side is converted to the type of the left, which is the
type of the result.
Type Conversions
Page 87
Consider the following assignments:
int i;
char c;
i = c;
In the aforesaid assignment, the data type on the right (char) is converted
to the data type on the left (int), which is the type of the result.
If x is float and i is int, then x = i and i = x both cause conversions; float
to int causes truncation of any fractional part. When a double is
converted to float, whether the value is rounded, or truncated is
implementation-dependent.
Explicit Type Conversion
Page 88
Finally, explicit type conversions can be forced
(“coerced”) in any expression, with a unary operator
called a cast.
In the construction (type name) expression, the
expression is converted to the named type by the
conversion rules above.
The precise meaning of a cast is as if the expression
were assigned to a variable of the specified type,
which is then used in place of the whole construction.
Explicit Type Conversion
Page 89
Consider the following example:
int i, j;
double d;
d = i / j; /* double assigned the result of the
division of two integers */
The problem with the above assignment is that the
fractional portion of the above division is lost, and
d is effectively assigned the integer quotient of the
two integers i and j.
Explicit Type Conversion
Page 90
To resolve this, the variable i can be typecast into a
double as in: d = (double)i / j;
int i is converted to a double using the explicit cast
operator. Since one of the operands is a double, the
other operand (j) is also converted to a double, and the
result is a double.
The result is assigned to a double with no side effects.
The cast operator can be done only on the right-hand
side of an assignment.
Ternary Operators
Page 91
if (a > b)
z = a;
else
z = b;
The aforesaid statements evaluate z to 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
Ternary Operators
Page 92
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; /* assign z the maximum of a
and b */
Compound Assignment Operators
Page 93
Compound assignment statements help in
simplifying, and writing simple code.
So far, we have been writing statements such as:
sum = sum + num
The same could be simplified as sum += num;
The same applies to the other binary operators.
Compound Assignment Operators
Page 94
sum = sum – num; can be written as sum -= num;
x = x * y; can be written as x *= y;
x = x / y; can be written as x /= y;
Compound assignment operators can be very useful
if the identifiers used for the operands is very long.
Page 95
Chapter 2
One-Dimensional Arrays
Page
96
One-Dimensional Arrays
Page 97
An array can be defined as a collection of elements of
identically typed data items that are stored
contiguously in memory.
Each array element shares the same name as the array
name, but distinguishes itself from other elements in the
array using a subscript, or an index.
The subscript, or the index of each array element is
determined based on the number of offset positions it is
from the starting position. The starting offset is taken as
0.
Array Representation
The declaration int a[10]; defines an array a of size 10, as a block of 10
contiguous elements in memory.
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
Page 98
Character Arrays
Page 99
To define a character array for storing a string of n characters, we
would need to define a character array of size n+1 characters.
This is because all character arrays are terminated by a NULL
character (‘\0’).
To define a character array called name for storing a ten-character
name, we will define the array as:
Char name[11];
where name[0] through name[9] will contain the characters comprising
the name, and name[10] will store the NULL character.
Representation of a Character Array
Page 100
name
a b c d e f g h i j \0
name[0] name[1] name[2] name[3] name[4] name[5] name[6] name[7] name[8] name[9] name[10]
Array Initialization
Page 101
Since an array is a set of elements located at
contiguous memory locations, its initialization involves
moving element by element, or one data at a time into
the array.
An array is initialized by specifying each element in an
initialization list separated by commas.
The size of the array, in this case, may or may not be
specified. The number of elements stored in the array
determines its size.
Array Initialization Syntax
Page 102
A character array needs a string terminator, the
NULL character (‘\0’) as the last character, whereas
integer and float arrays do not need a terminator.
#include<stdio.h>
main( )
{
char array1[ ] = {‘A’, ‘R’, ‘R’, ‘A’, ‘Y’, ‘\0’};
char array2[ ] = {“ARRAY”};
char dayofweek[ ] = {‘M’, ‘T’, ‘W’, ‘T’, ‘F’, ‘S’, ‘S’, ‘\0’};
float values[ ] = {100.56, 200.33, 220.44, 400.22, 0};
}
Array Processing
Page 103
#include<stdio.h>
main( )
{
char array1[ ] = {‘A’, ‘R’, ‘R’, ‘A’, ‘Y’, ‘\0’};
char array2[ ] = {“ARRAY”};
char dayofweek[ ] = {‘M’, ‘T’, ‘W’, ‘T’, ‘F’, ‘S’, ‘S’, ‘\0’};
float values[ ] = {100.56, 200.33, 220.44, 400.22, 0};
int i = 0;
printf( “String 1 is %s\n”, array1);
printf( “String 2 is %s\n”, array2);
for( i = 0; dayofweek[i] != ‘\0’; i = i +1)
printf ( “The Day %d in a week is %c\n”, i + 1, dayofweek[i];
for( i = 0; values[i] != 0; i = i +1)
printf ( “The amount %d in a week is %f\n”, i + 1, values[i];
}
Array Initialization Using a for Loop
Page 104
#include<stdio.h>
main( )
{
int i, num[50];
for (i = 0; i < 50; i = i + 1)
{
num[i] = 0;
num[i] = i + 1;
printf("%d\n",i);
}
}
Array Manipulation Using Subscripts
Page 105
#include<stdio.h>
/* displays each element of the array on a new line */
main( )
{
int i;
char array[11];
printf( "enter a string of maximum 10 characters\n");
gets(array);
fflush(stdin);
for (i = 0; array[i] != '\0'; i = i +1)
printf("Element %d is %c\n", i +1, array[i]);
}
Array Manipulation Using Subscripts
Page 106
/* this function finds the length of a character string */
#include <stdio.h>
main( )
{
int i = 0;
char string[11];
printf(“Enter a string of maximum ten characters\n”);
gets(string);
fflush( stdin);
for(i =0; string[i] != ‘\0’; i = i + 1)
;
printf(“The length of the string is %d \n”, i);
}
Array Manipulation Using Subscripts
Page 107
/* this function converts a string to upper case */
#include <stdio.h>
main( )
{
char string[51];
int i = 0;
printf("Enter a string of maximum 50 characters\n");
gets(string);
fflush(stdin);
while (string[i] != '\0')
{
if(string[i] >= 'a' && string[i] <= 'z')
{ string[i] = string[i] - 32;
i = i + 1; }
}
printf("The converted string is %s\n", string); }
Array Manipulation Using Subscripts
Page 108
#include <stdio.h>
main( )
{
int i, start_pos, no_of_chars;
char string[101], substring[101];
printf("Enter a string of upto 100 characters\n");
gets(string); fflush( stdin);
printf("Enter start position\n");
scanf("%d", &start_pos); fflush(stdin);
printf("Enter no. of characters to extract\n");
scanf("%d", &no_of_chars); fflush(stdin);
start_pos = start_pos -1;
for (i = 0; i < no_of_chars; i = i + 1, start_pos = start_pos + 1)
substring[i] = string[start_pos];
substring[i] = '\0';
printf("The substring is %s\n", substring);
}
Array Manipulation Using Subscripts
Page 109 #include<stdio.h>
main( )
{
int total=0, int_array[20], i = -1;
do
{
i = i + 1;
printf("Enter number(0 to terminate)\n");
scanf("%d", &int_array[i]);
}while (int_array[i] != 0);
i = 0;
while (int_array[i] != 0)
{
printf("Element number %d is %d\n", i + 1, int_array[i]);
total = total + int_array[i]; i = i + 1;
}
printf("The sum of the numbers in the array is %d \n", total);}
Array Addressing
Page 110
In the declaration:
char string[11);
the name of the array refers to the starting address
of the area that gets allocated for storing the
elements of the array.
Thus, string contains the address of string[0].
In the aforesaid declaration, string refers to the
starting position of the array, and the subscript refers
to the offset position from the starting position.
Array Addressing
Page 111
string
100
100 101 102 103 104 105 106 107 108 109 110
a b c d e f g h i j \0
0 1 2 3 4 5 6 7 8 9 10
Page 111
Array Addressing
Page 112
In the previous declaration, string represents the
starting point of the array, and the subscript refers to
the offset position from the starting point.
To arrive at the address of the particular element, the
compiler applies the following simple formula:
starting address of the array + ( offset position * size of
data type)
Address of element 4 = 100 + ( 3 * 1) = 100 +3 = 103
Summary
Page 113
In this session, you learnt to:
Write compound conditions employing the logical
operators
Write functions that perform formatted
input/output
Declare, initialize, manipulate, and address one-
dimensional arrays
Page
114
Page 115
Chapter 3
Two-Dimensional Arrays
Two-Dimensional Arrays
Page 116
While a one-dimensional array can be visualized in
one-dimension as either a row of elements, or a column
of elements, a two-dimensional array needs to be
visualized along two dimensions, i.e., along rows and
columns.
To be precise, a two-dimensional array can be
represented as an array of m rows by n columns.
A general way of representing or visualizing a two-
dimensional array is in the form of a two-dimensional
grid. But, in memory, even a two-dimensional array is
arranged contiguously as an array of elements.
Two-dimensional Arrays
Page 117
col 0 col 1 col 2
row 0
row 1
row 2
r0,c0 r0,c1 r0,c2 r1,c0 r1,c1 r1,c0 r2,c0 r2,c1 r2,c2
Two-Dimensional Arrays
Page 118
The Scenario: Consider a situation where you want
to record the region-wise, product-wise figures of
sales. The regions are A, B and C. The products are
X, Y and Z
Sales data for the region-wise, product-wise
breakup should be shown as follows:
Two-Dimensional Arrays
Page 119
Prod X Prod Y Prod Z
reg A
reg B
reg C
Declaring a Two-Dimensional Array
Page 120
Declaring a two-dimensional array involves two indices, or two
subscripts. There will be an extra set of square brackets[ ] to
indicate the second subscript, or the second index.
To declare a two-dimensional array for accommodating sales
data for three regions and three products, we would come out
with an array declaration as in:
int rp_array[3][3];
/* this array would have nine elements starting at
rp_array[0][0], rp_array[1][1]…….and going on till
rp_array[2,2] */
Initializing Two-Dimensional Arrays
Page 121
int rp_array[3][3] = {0,0, 0,
0, 0, 0,
0, 0, 0
};
To improve the legibility of the initialization, it can be
written as: int rp_array[3][3] = { {0,0, 0},
{0, 0, 0},
{0, 0, 0}
};
Initializing Two-Dimensional Arrays Using the for Loop
Page 122
/* r_counter is for referring to the rth region */
/* p_counter is for referring to the pth product */
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
rp_array[r_counter][p_counter] = 0;
}
}
Input to Two-Dimensional Arrays
Page 123
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
printf( “\nEnter sales data for Region %d and Product %d,
r_counter + 1, p_counter + 1)
scanf(“%d”, &rp_array[r_counter][p_counter]);
fflush( stdin);
}
}
Processing Two-Dimensional Arrays
Page 124
/* Program for converting these sales figures into percentages of total sales. */
main( )
{
int r_counter, p_counter, rp_array[3][3], total_sales = 0;
float rp_array_perc[3][3];
/* initialization of rp_array using the for loop */
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
rp_array[r_counter][p_counter] = 0;
}
}
Processing Two-Dimensional Arrays
Page 125
/* input sales into rp_array using the for loop */
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
printf( “\nEnter sales data for Region %d and Product %d,
r_counter + 1, p_counter + 1)
scanf(“%d”, &rp_array[r_counter][p_counter]);
fflush( stdin);
}
}
Processing Two-Dimensional Arrays
Page 126
/* Determine total sales using the for loop */
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
total_sales += rp_array[r_counter][p_counter];
}
}
Processing Two-Dimensional Arrays
Page 127
/* Determine percentage of individual sales data against total sales */
for (r_counter = 0; r_counter < 3; r_counter ++)
{
for (p_counter = 0; p_counter < 3; p_counter ++)
{
rp_array_perc[r_counter][p_counter] = (( float) 100 *
rp_array[r_counter][p_counter] ) / total_sales ;
}
}
} /* end of main( ) */
The Preprocessor Phase
Page 128
The measure of the flexibility of any program is the
ease with which changes can be implemented.
The preceding exercises involving two-dimensional
arrays is inflexible because the number of regions and
products is hard coded into the program, causing the
size of the array to be also hard coded.
If changes in terms of the number of regions and
products were to be incorporated with the least amount
of code maintenance, the best solution would be the use
of macros referred to in C as #define.
The Preprocessor Phase
Page 129
The #define constitutes the preprocessor phase, wherein the value/s specified as
part of the macro or #define is substituted into the code prior to compilation. This is
also known as macro substitution.
#include <stdio.h>
#define RG 3
#define PR 3
main( )
{
int r_counter, p_counter, rp_array[RG][PR], total_sales = 0;
float rp_array_perc[RG][PR];
/* initialization of rp_array using the for loop */
for (r_counter = 0; r_counter < RG; r_counter ++)
{
for (p_counter = 0; p_counter < PR; p_counter ++)
{
rp_array[r_counter][p_counter] = 0;
}
}
Two-Dimensional Character Arrays
Page 130
If you were to store an array of 11 names, with each
name containing up to a maximum of 30 characters,
you would declare it as a two-dimensional character
array such as: char name[11][31];
Here, name[0] through name[9] would store character
strings representing names of 30 characters each.
The first index represents the number of names, and the
second index represents the maximum size of each
name.
Initializing Two-Dimensional Character Arrays
Page 131
char team_india [11][30] = { “Akash Chopra”,
“Virendra Sehwag”,
“Rahul Dravid”
“Sachin Tendulkar”,
“V.V.S. Laxman”,
“Yuvraj Singh”,
“Ajit Agarkar”,
“Parthiv Patel”,
“Anil Kumble”,
“L. Balaji”,
“Irfan Pathan”
};
Printing Two-Dimensional Character
Arrays
Page 132
main( )
{
char team_india [11][30] = { “Akash Chopra”,
“Virendra Sehwag”,
“Rahul Dravid”
“Sachin Tendulkar”,
“V.V.S. Laxman”,
“Yuvraj Singh”,
“Ajit Agarkar”,
“Parthiv Patel”,
“Anil Kumble”,
“L. Balaji”,
“Irfan Pathan”
};
Printing Two-Dimensional Character
Arrays
Page 133
int i;
for( i = 0; i < 11; i ++)
{
printf(“%s”, team_india[i]);
}
}
Particular elements can also be printed from a two dimensional array by
using the second subscript.
Printf(“%c”, team_india[10][6] would print the character ‘P’ from the string
“Pathan”
Page
134
Page 135
Chapter 4
Pointers
Objectives
Page 136
At the end of this session, you should be able to:
Declare, initialize, and manipulate pointers
Use pointers for manipulating one-dimensional and
two-dimensional arrays
Distinguish between arrays and pointers
Use pointer arithmetic
Pointer – Definition
Page 137
When a variable is declared (such as int i = 22) in a
program, space is reserved in memory for the variable i.
To be precise, each variable is assigned a particular
memory location referenced by its address.
In our case, the integer i would be stored at a specific
memory location having the address, say, 1000 (addresses
are typically hexadecimal values).
The declaration of i can be conceptualized as follows:
Pointer – Definition
Page 138
i variable name i is a named location in memory that can hold
an integer and having the address 1000
22 value
1000 address
Pointer – Definition
Page 139
In C, it is possible to manipulate a variable either by its
name, or by its address. The address of a variable can
be stored in another variable (called a pointer
variable), and access can be had to the variable
through this pointer variable.
A pointer can therefore be defined as a variable that
holds the address of another variable.
If you want to define a pointer to hold the address of
an integer variable, then you must define the pointer as
a pointer to an integer.
Pointer – Declaration & Initialization
Page 140
It is in this sense that a pointer is referred to as a
derived data type, as the type of the pointer is
based on the type of the data type it is pointing to.
For the earlier declaration of the integer variable i, its
pointer can be declared as follows:
int i, *pointer_to_an_integer;
i = 22;
pointer_to_an_integer = &i; /* initializing the integer
pointer variable (pointer_to_an_integer) with the
address of the integer variable i. */
Pointer – Declaration & Initialization
Page 141
The ‘*’ operator precedes a pointer operand. In the preceding
declaration, int *pointer_to_an_integer; the ‘*’ operator preceding the
variable name marks it out as a pointer declaration, and the data type
of the pointer variable begins the pointer declaration.
It is obvious from the preceding code snippet that the ‘&’ operator is
used to return the address of a variable. The returned address is
stored into a pointer variable of the appropriate type.
It is critical, as a good C programmer, to initialize a
pointer as soon as it is declared.
Pointer – Declaration & Initialization
Page 142
the integer pointer variable (pointer_to_an_integer)
being initialized with the address of the integer
variable i.
i variable name
pointer_to_an_integer 22 variable value
1000 1000 variable address
Dereferencing a Pointer
Page 143
Returning the value pointed to by a pointer is known
as pointer dereferencing. To deference the contents
of a pointer, the * operator is used.
#include<stdio.h>
main( )
{
int x, y, *pointer;
x = 22;
pointer = &x;
y = *pointer; /* obtain whatever pointer is pointing to */
}
Pointers - Variations on a theme
Page 144
pointer = &x; /* make pointer point to x */
y = *ptr + 1; /* return the value of the variable
pointed to by pointer, add 1 to it, and store the
result in y */
*ptr = 0; /* set the value of the variable pointed
to by pointer to 0. In this case, the variable x is set
to 0 through its pointer */
Pointers - Variations on a Theme
Page 145
Consider the following code snippet:
Int x, y, *pointer1, *pointer2;
pointer1 = &x /* return the address of x into pointer1 */
pointer2 = pointer1; /* assign the address in pointer1 to pointer2.
Hence, both the pointer variables, pointer1 and pointer2, point to
the variable x. */ i variable name
pointer1 22 variable value
1000 1000 variable address
pointer2
1000
Pointers - Variations on a Theme
Page 146
Consider the following code snippet:
int x, y, *p1, *p2;
x = 22;
y = 44;
p1 = &x; /* p1 points to x */
p2 = &y /* p2 points to y */
*p1 = *p2 /* make whatever p1 was pointing to
(variable x and hence the value 22) equivalent to
whatever p2 is pointing to. */
Hence, both x and y will now have the value 44.
Pointers - Variations on a Theme
Page 147
Before *p1 = *p2
p1 x
1000 22
p2 y
2000 44
After *p1 = *p2
pointer1 x
1000 44
pointer2
y
1000 44
Printing Using Pointers
Page 148
Consider the following code snippet:
#include <stdio.h>
main( )
{
int x, *p;
x = 26;
p = &x;
printf("The value of x is %d\n", x);
printf("The value of x is %d\n", *p);
}
One-dimensional Character Arrays Using Pointers
Page 149
In the declaration:
char string[11];
the name of the array refers to the starting address
of the area that gets allocated for storing the
elements of the array.
Thus, string contains the address of string[0].
Since a pointer is a variable that contains the address
of another variable, it is evident that string is a
pointer.
One-dimensional Character Arrays Using Pointers
Page 150
What is string pointing to? Seemingly, it points to a
string, but actually, string is pointing to a character at
string[0].
Recall that a string is just a sequence of characters
terminated by a null character (‘\0’).
When the string name is passed down as a parameter
to a printf( ) function, it starts printing from the starting
address of the string till it encounters a null character
(‘\0’), which happens to be the string terminator.
Difference Between Pointers and
Page 151
Arrays
There are subtle differences between pointers and
arrays.
Consider the following declaration:
char string[10], *p;
Both string and p are pointers to char.
Difference Between Pointers and
Page 152
Arrays
However, string[10] has 10 bytes of contiguous
storage allocated for it.
Thus, during execution, string is effectively a pointer
with a constant address, namely, the address
&string[0];
And this address cannot be changed during the life
of the program
Difference Between Pointers and
Page 153
Arrays
However, p is a pointer variable that can point to one
string at one point of time during the running of the
program, and can also point to another string at
another point of time during the running of the same
program.
p is a variable, and a variable by its very definition can
hold different values at different points of time during
program execution.
Therefore, we can conclude that a string is a pointer
constant, whereas an explicit character pointer
declaration is a character pointer variable.
One-dimensional Character Arrays Using Pointers
Page 154
The one-dimensional character array declared
earlier (char string[11]) can be alternately
declared as:
char *string; /* string is now a explicit pointer variable that can point to a
character */
char *string = “Hello”;
printf(“%s”, string);
Two-dimensional Character Arrays Using Pointers
Page 155
Since a pointer to a character can be a pointer to a
string, it follows therefore that a two-dimensional
character array can be declared as an array of
character pointers.
Recall the declaration of the two dimensional
character array that you used earlier to store 11
names, each of which could be a maximum of 30
characters. It was written as: char team_india
[11][30];
Two-dimensional Character Arrays Using Pointers
Page 156
This could be alternately declared as:
char *team_india[11];
team_india is now an array of 11 character pointers,
each of which in turn can point to a string. A pointer to
a character can be a pointer to a string as well.
The flexibility with a declaration of an array of
character pointers is that each of the character pointers
may point to an array of unequal length, as against the
declaration of a two-dimensional character array in
which each string is of a fixed size.
Pointer Arithmetic
Page 157
The most common arithmetic operation using pointers is
incrementing by 1. Consider the following statement:
char *p = “Sherlock H”;
printf(“%s\n”, p);
The initialization of the character pointer with the string
“Sherlock H” can be visualized as shown in the following
slide.
Pointer Arithmetic
Page 158
100
100 101 102 103 104 105 106 107 108 109 110
S h e r l o c k H \0
0 1 2 3 4 5 6 7 8 9 10
Pointer Arithmetic
Page 159
Now, let us increment the pointer p by 1, as in:
p++;
p initially pointed to the base address of the string
“Sherlock H”, i.e., 100.
After incrementing the pointer p by 1, it points to the next
element in the string or the character array, i.e., character
‘h’ after ‘S’.
p now contains the address of the element ‘h’, i.e., 101
Pointer Arithmetic
Page 160
101
100 101 102 103 104 105 106 107 108 109 110
S h e r l o c k H \0
0 1 2 3 4 5 6 7 8 9 10
Pointer Arithmetic
Page 161
But will the statement p++; always make p point to the
next memory location.
The answer is an emphatic No.
This depends upon the data type that p is pointing to.
Consider the following piece of code:
#include <stdio.h>
main( )
{
Pointer Arithmetic
int int_array[3] = {4, 8, 22}; /* int_array is a constant pointer */
int * p;
p = int_array;
printf (“%d”, p);
p++;
printf(“%d”, p); The value of p afer being initialized
} p with the address of int_array
100
100 104 108
4 8 12
Page 162
Pointer Arithmetic
Page 163
The value of p after pointer arithmetic
performed on p, i.e., p++
p
104
100 104 108
4 8 12
Pointer Arithmetic
Page 164
The key point to note is that when pointers are
incremented by 1, the size of the data type to
which it is pointing to (4 bytes in our case, since an
integer needs 4 bytes of storage) is taken into
account.
To summarize, the operation p++; will result in the
following computation:
New address of p = old address of p + size of
data type
Pointer Arithmetic
Page 165
Consider the following declaration of a two-dimensional
integer array:
int p[3][5] = {
{ 2, 4, 6, 8, 10},
{ 3, 6, 9, 12, 15},
{ 5, 10, 15, 20, 25}
};
The aforesaid declaration declares an array of three
integer pointers, each pointing to the first element of an
array of 5 integers. This can be visualized as follows:
Pointer Arithmetic
Page 166
p
100
200 204 208 212 216
100
200
2 4 6 8 10
300 304 308 312 316
104 300 3 6 9 12 15
400 404 408 412 416
108 400
5 10 15 20 25
Pointer Arithmetic
Page 167
Here, p points to the first element of the array of pointers.
*p equals p[0], i.e., it returns the address 200. This address
points to the element at offset [0,0], i.e., element 2.
Therefore, *(*p) returns the value 2.
Since p is a pointer to another pointer, incrementing p by 1
makes it point to the next element in the array of pointers,
i.e., it now points to the element containing the address 300.
Pointer Arithmetic
Page 168
Hence, *(p + 1) returns the address 300.
Therefore, * (*(p + 1)) returns the value at this
address, i.e., the element with the value 3. In other
words, the element at the offset [1,0].
The following table gives various pointer
expressions, and their values:
Pointer Arithmetic
Page 169
Pointer Resulting Variable Value
Expression Address
*(*p) 200 p[0][0] 2
*(*p+1) 204 p[0][1] 4
*(*(p + 1)) 300 p[1][0] 3
*(*(p+1)+1) 304 p[1][1] 6
*(*(p+1)+1)+1 304 p[1][1] + 1 6+1=7
Page 169
String Handling Functions Using
Pointers
Page 170
/* The following function determines the length of a string */
#include <stdio.h>
main( )
{
char *message = “Virtue Alone Ennobles”;
char *p;
int count;
for (p = message, count = 0; p != ‘\0’; p++)
count++;
printf(The length of the string is %d\n”, count);
}
String Handling Functions Using Pointers
Page 171
/* The following functions compares two strings */
#include<stdio.h>
main( )
{
char *message1 = “This is a test”;
char *message2 = “This is not a test”;
char *p1, *p2;
for(p1=message1, p2=message2; (*p1 = = *p2) && (*p1 != ‘\0’) && (*p2
!= ‘\0’); p1++, p2++)
if ((*p1 = = ‘\0’) && (*p2 = = ‘\0’))
printf(“The two strings are identical\n”);
else
printf(“The two strings are not identical\n”);
}
Page
172
Page 173
Chapter 5
The Function Call Mechanism
Objectives
Page 174
In this session, you will learn to:
Write programs that invoke functions through a:
Call by value
Call by reference
Define function prototypes
Describe the function call mechanism
Pass arguments to main( ) in the form of command-line
arguments
Use the Auto, Static, and Extern storage qualifiers
Use string handling functions, conversion functions, and
functions for formatting strings in memory
Advantages of Function
Page 175
Functions facilitate the factoring of code. Every C program
consists of one main( ) function typically invoking other
functions, each having a well-defined functionality.
Functions therefore facilitate:
Reusability
Procedural abstraction
By procedural abstraction, we mean that once a function is
written, it serves as a black box. All that a programmer
would have to know to invoke a function would be to know
its name, and the parameters that it expects.
Function Parameters
Page 176
Function parameters are defined as part of a function
header, and are specified inside the parentheses of the
function.
The reason that functions are designed to accept
parameters is that you can embody generic code inside
the function.
All that would be required by the function would be the
values that it would need to apply the generic code on
the values received by it through its parameters.
Function Parameters
Page 177
Consider the following printf( ) statement:
printf(“%d”, i);
This is actually a call to the printf( ) function with two
pieces of information passed to it, namely, the format
string that controls the output of the data, and the
variable that is to be output.
The format string, and the variable name can be called
the parameters to the function printf( ) in our example.
Function Parameters
Page 178
Function parameters are therefore a mechanism
wherein values can be passed down to a function for
necessary processing, and the processed value returned
back from the function, as the case may be.
It is important to remember that not all function need be
defined to accept parameters, though most functions do.
This brings us now to the important concept of a
function returning a value, and also the return type
of a function.
Invoking Functions
Page 179
In C, functions that have parameters are invoked in
one of two ways:
Call by value
Call by reference
Call by Value
Page 180
void swap(int,int );
main()
{ int a=10, b=20;
swap(a, b);
printf(“ %d %d \n”,a,b);
}
void swap (int x, int y)
{ int temp = x;
x= y;
y=temp;
}
Call by Value
Page 181
In the preceding example, the function main( ) declared and
initialized two integers a and b, and then invoked the function swap(
) by passing a and b as arguments to the function swap( ).
The function swap( ) receives the arguments a and b into its
parameters x and y. In fact, the function swap( ) receives a copy of
the values of a and b into its parameters.
The parameters of a function are local to that function, and hence,
any changes made by the called function to its parameters affect
only the copy received by the called function, and do not affect the
value of the variables in the called function. This is the call by value
mechanism.
Call by Reference
Page 182
Call by reference means that the called function should
be able to refer to the variables of the calling function
directly, and not create its own copy of the values in
different variables.
This would be possible only if the addresses of the
variables of the calling function are passed down as
parameters to the called function.
In a call by reference, therefore, the called function
directly makes changes to the variables of the calling
function.
Call by Reference
Page 183
Consider the same swap( ) that we discussed earlier now rewritten using a call
by reference.
void swap( int *, int *);
main()
{ int a=10, b=20;
swap(&a, &b);
printf(“ %d %d \n”,a,b);
}
void swap (int *x, int *y)
{ int temp=*x;
*x=*y;
*y=temp;
}
Passing Arrays to Functions
Page 184
Arrays are inherently passed to functions through a call
by reference.
For example, if an integer array named int_array of
10 elements is to be passed to a function called fn( ),
then it would be passed as:
fn( int_array);
Recall that int_array is actually the address of the
first element of the array, i.e., &int_array[0].
Therefore, this would be a call by reference.
Passing Arrays to Functions
Page 185
The parameter of the called function fn( ) would
can be defined in one of three ways:
fn( int num_list[ ]); or
fn(int num_list[10]); or
fn(int *num_list)
Returning a Value From a Function
Page 186
Just as data can be passed to a function through the
function’s parameters at the time of call, the function
can return a value back to the caller of the function.
In C, functions can return a value through the return
statement. Consider the following example:
#include<stdio.h>
main( )
{
int i,j, value;
scanf(“%d %d”, &i, &j);
fflush(stdin);
Returning Value From a Function
Page 187
value = add(i, j);
printf( “the total is %d\n”, value);
}
add( int a, int b)
{
return (a + b);
}
In the aforesaid example, the function add( ) sends back the value of the
expression (a + b) to the function main( ). The value returned to main( ) from
add( ) is stored in the variable value which appears on the left hand side
of the statement in which the function add( ) is called.
Returning a Value From a Function
Page 188
The return statement not only returns a value back
to the calling function, it also returns control back to
the calling function.
A function can return only one value, though it can
return one of several values based on the
evaluation of certain conditions.
Function Prototype
Page 189
C assumes that a function returns a default value of int
if the function does not specify a return type.
In case a function has to return a value that is not an
integer, then the function itself has to be defined of the
specific data type it returns.
Consider the following code:
#include <stdio.h>
main( )
{
Function Prototype
Page 190
Functions should be declared before they are used.
Consider the situation where you want to use the pow( )
function, called the power function, one of many
functions in the mathematics library available for use by
the programmer.
A function call such as pow(x, y) returns the value of x
raised to the power y.
To elucidate further, pow(2.0, 3.0) yields the value 8.0
Function Prototype
Page 191
The declaration of the function is given by:
double pow( double x, double y);
Function declarations of this type are called function
prototypes.
An equal function prototype is given by:
double pow( double, double);
Function Prototype
Page 192
A function prototype tells the compiler the number
and data types of arguments to be passed to the
function and the data type of the value that is to be
returned by the function.
ANSI C has added the concept of function
prototypes to the C language.
Function Prototype
Page 193
float add(float a, float b);
float i, j, value;
scanf(“%f %f”, &i, &j);
fflush(stdin);
value = add(i, j);
printf( “the total is %d\n”, value);
}
float add(float a, float b)
{
return (a + b);
}
Function Prototype
Page 194
#include <stdio.h>
main( )
{
void add( float, float);
float i, j, value;
scanf(“%f %f”, &i, &j);
fflush(stdin);
add(i, j);
printf( “the total is %d\n”, value);
}
void add(float a, float b)
{
printf(“%f”, a + b);
return;
}
Function Calls
Page 195
It is important for us to know what happens under the hood when a
function executes.
A logical extension of the aforesaid point is the situation that arises
when a function calls another function.
It is important to know how the CPU manages all this, i.e., knowing
where to look for when a function call is encountered, and having
executed the function, to also know where it has to return to.
In short, we need to know the call mechanism, and the return
mechanism.
Function Calls – A Top Level
Page 196
Overview
When a function call is encountered, it involves the
following steps:
1. Each expression in the argument list is evaluated.
2. The value of the expression is converted, if
necessary, to the type of the formal parameter,
and that value is assigned to the corresponding
formal parameter at the beginning of the body of
the function.
3. The body of the function is executed.
Function Calls – A Top Level
Page 197
Overview
4. If the return statement includes an expression, then
the value of the expression is converted, if
necessary, to the type specified by the type
specifier of the function, and that value is passed
back to the calling function.
5. If no return statement is present, the control is
passed back to the calling function when the end
of the body of the function is reached. No useful
value is returned.
Function Calls & The Runtime Stack
Page 198
Runtime Environment: Runtime Environment is the
structure of the target computer’s registers and memory
that serves to manage memory and maintain the
information needed to guide the execution process.
The C language uses a stack-based runtime
environment, which is also referred to as a runtime
stack, or a call stack.
Let us begin by understanding the internal memory
organization that comes into the picture whenever a
program needs to be executed.
Function Calls & The Runtime Stack
Page 199
Memory
Register Area
Code Area
RAM
Data Area
Code Area
Page 200
Entry point for procedure 1
Code for
Procedure 1
Entry point for procedure 2
Code for
Procedure 2
.
.
Entry point for procedure n
Code for
Procedure n
Data Area
Page 201
Only a small part of data can be assigned fixed
locations before execution begins
Global and/or static data
Compile-time constants
◼ Large integer values
◼ Floating-point values
Literal strings
Dynamic Memory
Page 202
The memory area for the allocation of dynamic
data can be organized in many different ways.
A typical organization divides the dynamic memory
into
stack area (LIFO protocol)
heap area
Memory Organization
Page 203
code area
global/static area
stack
free space
heap
Procedure Activation Record
Page 204
Procedure activation record
contains memory allocated for
the local data of a procedure or
function when it is called, or A Procedure Activation
activated. Record or a Stack Frame
arguments
When activation records are
kept on stack, they are called bookkeeping information
stack frames. Details depend on (return address)
the architecture of target local data
machine and properties of the
language. local temporaries
Registers
Page 205
Registers may be used to store temporaries, local
variables, or even global variables.
When a processor has many registers, the entire
static area and whole activation records may be
kept in the registers.
Special purpose registers:
Program counter (PC)
Stack pointer (SP)
Calling Sequence
Page 206
The calling sequence is the sequence of operations
that must occur when a procedure or function is
called.
Allocation of memory for the activation record
The computation and storing the arguments
Storing and setting registers
Return Sequence
Page 207
The return sequence is the sequence of operations
needed when a procedure or function returns.
The placing of the return value where it can be accessed by
the caller
Readjustment of registers
Releasing of activation record memory
Fully Static Runtime Environment
Page 208
The simplest kind of a runtime environment.
All data are static, remaining in memory for the duration of
the program.
All variables can be accessed directly via fixed addresses
Each procedure has only a single activation record, which is
allocated statically prior to execution.
Such environment can be used to implement a language in
which:
There are no pointers or dynamic allocation,
Procedures cannot be called recursively.
Example: COBOL & FORTRAN
Stack-based Runtime Environment
Page 209
In a language in which recursive calls are allowed, activation
records cannot be allocated statically.
Activation records are allocated in a stack-based fashion.
This stack is called the stack of activation records (runtime
stack, call stack).
Each procedure may have several different activation records
at one time.
Global Procedures
Page 210
In a language where all procedures are global (the C language),
a stack-based environment requires two things:
1. A pointer to the current activation record to allow access to
local variables.
– This pointer is called the frame pointer (fp) and is usually
kept in a register.
2. The position or size of the caller’s activation record
– This information is commonly kept in the current activation
record as a pointer to the previous activation record and
referred as the control link or dynamic link.
– Sometimes, the pointer is called the old fp
3. Additionally, there may be a stack pointer (sp)
– It always points to the top of the stack
Tracing Function Calls
Page 211
int z;
fn_a( int m ) fn_b( int n )
main( )
{ { {
int x; int y; int b;
fn_a( ); fn_b( ); …..
….. . return instruction
….. .
. return instruction
. . .
. . .
}
. }
}
A View of the Runtime Stack
Page 212
z
S Global static area
t Activation record of main
x
a
m
c
y
k
control link Activation record of call to fn_a( )
G return address
r n
o control link Activation record of call to fn_b( )
w return address
t fp b
sp
h Free Store (Heap)
Access to Variables
Page 213
In static environment, parameters and local
variables can be accessed by fixed addresses.
In a stack-based environment, they must be found
by offset from the current frame pointer.
In most languages, the offset for each local variable
is still statically computable by compiler.
The declarations of a procedure are fixed at compile
time and the memory size to be allocated for each
declaration is fixed by its data type.
Calling Sequence
Page 214
1. Compute the arguments and store them in their
correct positions in the new activation record
(pushing them in order onto the runtime stack)
2. Store (push) the fp as the control link in the new
activation record.
3. Change the fp so that it points to the beginning
of the new activation record (fp=sp)
4. Store the return address in the new activation
record.
5. Jump to the code of the procedure to be called.
Return Sequence
Page 215
1. Copy the fp to the sp
2. Load the control link into the fp
3. Jump to the return address
4. Change the sp to pop the arguments.
Variable Length-Data
Page 216
There is a possibility that data may vary, both in
the number of data objects and the size of each
object.
Two examples:
The number of arguments in a call may vary from call
to call.
The size of an array parameter or a local array
variable may vary from call to call.
Variable Length Data
Page 217
The printf( ) function in C, where the number of
arguments is determined from the format string that
is passed as the first argument.
printf(“%d%s%c”, n, prompt, ch);
printf(“Hello, world!”);
C compilers push arguments onto the stack in
reverse order.
The first parameter (which tells how many more
parameters are) is always located at the fixed
offset from fp.
Local Temporaries
Page 218
Local temporaries are partial results of
computations that must be saved across procedure
calls.
Example:
x[i]=(i+j)*(i/k+f(i));
Three partial results need to be saved: the address of
x[i], the sum i+j, and the quotient i/k.
They can be stored
1. In the registers
2. As temporaries on the runtime stack prior the call to f.
Nested Declarations
Page 219
Nested declarations can be treated in a similar way to temporary expressions, allocating them on
the stack as the block is entered and deleting them on exit.
void p (int x, double y)
{ char a;
int i;
{ double x; // block A
int y;
…
}
{ double x; // block B
int j;
…
}
{ char *a; // block C
int k;
…
}
}
Passing Arguments to main( )
Page 220
Arguments are generally passed to a function at the time of
call. And function calls are initiated by main( ).
Since main( ) is the first function to be executed, there is no
question of main( ) being called from any other function.
So, if main( ) is not going to be invoked by any other
function, is it possible to pass arguments to main( ) given the
fact that arguments are generally passed to a function at the
time of invocation.
The answer lies in understanding command-line arguments.
Command-Line Arguments
Page 221
The function main( ) can receive arguments from the
command line.
Information can be passed to the function main( )
from the operating system prompt as command line
arguments.
The command line arguments are accepted into
special parameters to main( ), namely, argc and
argv. Their declarations are as follows:
main(int argc, char * argv[ ])
Command Line Arguments
Page 222
argc provides a count of the number of command
line arguments.
argv is an array of character pointers of undefined
size that can be thought of as an array of pointers
to strings.
The strings are the words that make up the
command line arguments.
Since the element argv[0] contains the command
itself, the value of argc is at least 1.
Command Line Arguments
Page 223
Consider a program called uppercase which
converts a string to uppercase. The program
expects the string to be entered at the command
prompt.
It should be noted that if the string accepted as a
command line argument has embedded spaces, it
should be enclosed in quotation marks.
Assume the program is run by entering the following
command at the operating system prompt:
Uppercase “Sherlock Holmes”
Command-Line Arguments
Page 224
argv
argc = 3
100
argv[0]
200
u p p e r c a s e \0
argv[1] 300
S h e r l o c k H o l m e s \0
Command Line Arguments
Page 225
The program uppercase.c can be coded as follows:
#include <stdio.h>
main(int argc, char * argv[ ])
{
int i;
for (i = 0; argv[1][i] != ‘\0’, i++)
{
if ((argv[1][i] >= ‘a’) && (argv[1][i] <= ‘z’))
argv[1][i] = argv[1][i] – 32;
}
printf( “%s”, argv[1]);
}
Storage Qualifiers
Page 226
The storage qualifier determines the lifetime of the
storage
associated with the identified variable.
A variable also has a scope, which is the region of
the program in which it is known.
The storage qualifiers in C are:
auto
static
extern
register
automatic Variables
Page 227
automatic variables are local to a block, and are
discarded on exit from the block.
Block implies a function block, a conditional block,
or an iterative block.
Declarations within a block create automatic
variables if no storage class specification is
mentioned, or if the auto specifier is used.
Variable declarations, therefore, are by default,
auto.
automatic Variables
Page 228 #include<stdio.h>
main( )
{
char var;
while ((var = getchar( )) != ‘*’)
{
if ((var >= ‘A’) && (var <= ‘Z’))
{
uppercase_count( );
}
}
}
uppercase_count( )
{
auto int counter = 0;
counter ++;
}
Global Variables
Page 229
Global variable is defined outside all functions.
A typical convention of defining global variables is
before the main( ) function.
A variable declared as global is visible to the
function main( ) as well as to any other functions
called from main( ).
A variable defined as global has file scope, that is,
it is visible to all the functions written within the
scope of a program file.
Global Variables
Page 230
/* A sample C program */
# include <stdio.h>
int sum( ); /* function prototype */
int a=10, b=20; /* a and b are global variables, visible to main( ) as well as to sum( )
*/
main()
{
int c;
c = sum();
printf(“%d+%d = %d \n”,a,b,c);
}
int sum()
{
return(a+b);
}
Static Variables
Page 231
Static variables may be local to a block or external
to all blocks, but in either case retain their values
across exit from, and reentry to functions and
blocks.
Within a block, including a block that provides the
code for a function, static variables are declared
with the keyword static.
Let us rewrite the code for the example involving
auto variables to incorporate the declaration of
static variables.
Static Variables
Page 232
#include<stdio.h>
main( )
{
char var;
while ((var = getchar( )) != ‘*’)
{
if ((var >= ‘A’) && (var <= ‘Z’))
{
uppercase_count( );
}
}
}
uppercase_count( )
{
static int counter = 0;
counter ++;
}
Static and Global Variables – A
Comparison
Page 233
From the preceding example, it seems that static
variables are functionally similar to global variables
in that they retain their value even after exiting from
a function in which it has been defined as static.
But an important difference is that while a global
variable has file scope, and is therefore visible to all
functions defined within that program file, a static
variable is visible only to the function in which it has
been defined as static.
Extern Variables
Page 234
Variables declared as extern are useful especially
when building libraries of functions.
This means that functions required to run a program
can be saved in separate program files, which can
be compiled separately, and linked up with the file
containing the function main( ) prior to running the
program.
In such a case, if the functions use the same global
variables, then their declarations would involve the
use of the extern qualifier
Extern Variables
Page 235
Program a.c Program b.c
int val /* global */ compute( )
main( ) {
{ extern int val; /* implies that
val is defined in another
printf(“Enter value”);
program containing a
scanf(“%d”, &val); function to which the
compute( ); /* function call */ current function will be
linked to at the time of
}
compilation */
}
Extern Variables
Page 236
From the preceding example, it is clear that though
val is declared global in the function main( ) in
program a.c, it has been declared again within the
function compute( ), which is defined in its own
program file, b.c.
However, the qualifier extern has been added to its
declaration.
The extern qualifier in the function compute( ) in b.c
indicates to the compiler (when a.c and b.c are
compiled together) that the variable used in this
program file has been declared in another program
file.
Standard String Handling
Page 237
Functions
strcmp( ) – compares two strings (that are passed to it as parameters)
character by character using their ASCII values, and returns any of the
following integer values.
Return Value Implication Example
Less than 0 ASCII value of the character of i = strcmp(“XYZ”, “xyz”)
the first string is less than the
ASCII value of the corresponding
character of the second string
Greater than 0 ASCII value of the character of i = strcmp(“xyz”, “XYZ”)
the first string is less than the
ASCII value of the corresponding
character of the second string
Equal to 0 If the strings are identical i = strcmp(“XYZ”, “XYZ”)
Page 237
Standard String Handling Functions
Page 238
strcpy( ) – copies the second string to the first
string, both of which are passed to it as
arguments.
Example: strcpy( string1, “XYZ”) will copy the string
“XYZ” to the string string1.
If string1 were to contain any value, it is overwritten
with the value of the second string.
Standard String Handling Functions
Page 239
strcat( ) – appends the second string to the first
string, both of which are passed to it as
arguments.
Example: assume that the string realname contains
the value “Edson Arantes Do Nascimento”. If one
were to append the nickname “Pele” to the string
realname, then it can be done as follows:
strcat(“Edson Arantes Do Nascimento”, “Pele”); will give
the string “Edson Arantes Do Nascimento Pele”
Standard String Handling Functions
Page 240
strlen( ) – This function returns the length of a
string passed to it as an argument. The string
terminator, i.e., the null character is not taken into
consideration.
Example: i = strlen(“Johann Cryuff”); will return the
value value 13 into i.
String to Numeric Conversion
Page 241
Functions
atoi( ) – This function accepts a string representation of an integer,
and returns its integer equivalent.
Example: i = atoi(“22”) will return the integer value 22 into the
integer i.
This function is especially useful in cases where main is designed to
accept numeric values as command line arguments.
It may be recalled that an integer passed as a command line
argument to main( ) is treated as a string, and therefore needs to be
converted to its numeric equivalent.
String to Numeric Conversion
Page 242
Functions
atof( ) - This function accepts a string representation of
a number, and returns its double equivalent.
For example, if the string str contains the value “1234”,
then the following statement:
i = atoi(str); will cause i to have the value 1234.000000
To use the function atof( ) in any function, its prototype
must be declared at the beginning of the function where
it is used:
double atof( )
Functions for Formatting Data in
Memory
Page 243
sprintf( ) – this function writes to a variable in
memory specified as its first argument.
The syntax of the function sprintf( ) is as follows:
sprintf( string, format specification, data)
Example: sprintf( str, “%02d-%02d-%02d”, 28, 8,
71) will return the string 28-08-71 into str.
Functions for Formatting Data in
Memory
Page 244
sscanf( ) – this function reads from a variable in
memory, and stores the data in different memory
variables specified.
The syntax of the function sscanf( ) is:
Sscanf( string, format specification, variable list);
Example: sscanf(str, “%2d%2s%s%d”, &day, suffix, mnth, &year)
printf(“The day is : %d, suffix is %s, month is %s, year is %d\n”, day, suffix,
mnth, year);
If the data in str is “29th July 1971”, the output will be:
The day is 29, suffix is th, month is July, year is 1971
Page
245
Page 246
Chapter 6
File Input/Output
Objectives
Page 247
In this session, you will learn to:
Use pointers of type FILE when performing file I/O
Perform Input/Output with files
Use character-based file input/output functions
Use string-based file input/output functions
Perform random access on files using the functions
fseek( )
ftell( )
rewind( )
feof( )
Files
Page 248
A collection of logically related information
Examples:
An employee file with employee names, designation, salary etc.
A product file containing product name, make, batch, price etc.
A census file containing house number, names of the members,
age, sex, employment status, children etc.
Two types:
Sequential file: All records are arranged in a particular order
Random Access file: Files are accessed at random
File Access
Page 249
The simplicity of file input/output in C lies in the fact
that it essentially treats a file as a stream of
characters, and accordingly facilitates input/output in
streams of characters.
Functions are available for character-based
input/output, as well as string-based input/output
from/to files.
In C, there is no concept of a sequential or an
indexed file. This simplicity has the advantage that
the programmer can read and write to a file at any
arbitrary position.
File Access
Page 250
The examples so far have involved reading from
standard input, and writing to standard output,
which is the standard environment provided by the
operating system for all applications.
You will now look at the process that a program
needs to follow in order to access a file that is not
already connected to the program.
Before a file can be read from, or written into, a
file has to be opened using the library function
fopen( )
File Access
Page 251
The function fopen( ) takes a file name as an
argument, and interacts with the operating system for
accessing the necessary file, and returns a pointer of
type FILE to be used in subsequent input/output
operations on that file.
This pointer, called the file pointer, points to a
structure that contains information about the file, such
as the location of a buffer, the current character
position in the buffer, whether the file is being read or
written, and whether errors, or end of file have
occurred.
File Access
Page 252
This structure to which the file pointer point to, is of
type FILE, defined in the header file <stdio.h>.
The only declaration needed for a file pointer is
exemplified by:
FILE *fp;
FILE *fopen(char *name, char *mode);
fp = fopen( “file name”, “mode”);
Once the function fopen( ) returns a FILE type
pointer stored in a pointer of type FILE, this pointer
becomes the medium through which all subsequent
I/O can be performed.
File Access Modes
Page 253
When opening a file using fopen( ), one also needs to mention the
mode in which the file is to be operated on. C allows a number of
modes in which a file can be opened.
Mode Access Explanation
“r” Read only mode Opening a file in “r” mode only
allows data to be read from the file
“w” Write only mode The “w” mode creates an empty file
for output. If a file by the name
already exists, it is deleted. Data can
only be written to the file, and not
read from it
Page 253
File Access Modes
Page 254
Mode Access Explanation
“a” Append mode Allows data to be appended to the end of the file,
without erasing the existing contents. It is therefore
useful for adding data to existing data files.
“r+” Read + Write This mode allows data to be updated in a file
mode
“w+” Write + Read This mode works similarly to the “w” mode, except
mode that it allows data to be read back after it is written.
“a+” Read + This mode allows existing data to be read, and new
Append mode data to be added to the end of the file.
Page 254
Character-based File I/O
Page 255
In C, character-based input/output from and to files
is facilitated through the functions fgetc( ) and
fputc( ).
These functions are simple extensions of the
corresponding functions for input/output from/to the
terminal.
The only additional argument for both functions is
the appropriate file pointer, through which these
functions perform input/output from/to these files.
A File Copy Program
Page 256
#include<stdio.h>
main( )
{
FILE *fp1, *fp2;
fp1 = fopen( “source.dat”, “r”);
fp2 = fopen( “target.dat”, “w”);
char ch;
while ( (ch = fgetc( fp1)) != EOF)
{
fputc (ch, fp2);
}
fclose(fp1);
fclose(fp2);
}
Variation to Console-Based I/O
Page 257
#include<stdio.h>
main( )
{
char ch;
while ( (ch = fgetc( stdin)) != EOF)
{
fputc (ch, stdout);
}
}
Nuggets on FILE Type Pointers
Page 258
The important point to note is that in C, devices are
also treated as files. So, the keyboard and the VDU
are also treated as files.
It would be interesting here to know what stdin,
stdout, and stderr actually are.
stdin, stdout, and stderr are pointers of type FILE
defined in stdio.h. stdin is a FILE type pointer to
standard input, stdout is a FILE type pointer to
standard output, and stderr is a FILE type pointer
to the standard error device.
Nuggets on FILE Type Pointers
Page 259
In case fopen( ) is unsuccessful in opening a file (file
may not exist, or has become corrupt), it returns a
null (zero) value called NULL.
NULL is defined in the header file stdio.h
The NULL return value of fopen( ) can be used for
error checking as in the following line of code:
if ((fp = fopen(“a.dat”, “r”)) = = NULL)
printf(“Error Message”);
The exit( ) function
Page 260
The exit( ) function is generally used in conjunction
with checking the return value of the fopen( )
statement.
If fopen( ) returns a NULL, a corresponding error
message can be printed, and program execution
can be terminated gracefully using the exit( )
function.
if ((fp = fopen(“a.dat”, “r”)) = = NULL)
{
printf(“Error Message”);
exit( );
}
Line Input/Output With Files
Page 261
C provides the functions fgets( ) and fputs( ) for
performing line input/output from/to files.
The prototype declaration for fgets( ) is given below:
char* fgets(char *line, int maxline, FILE *fp);
The explanations to the parameters of fgets( ) is:
char* line – the string into which data from the file is to
be read
int maxline – the maximum length of the line in the file
from which data is being read
FILE *fp – is the file pointer to the file from which data is
being read
Line Input/Output With Files
Page 262
fgets( ) reads the next input line (including the
newline) from file fp into the character array line;
At most maxline-1 characters will be read. The
resulting line is terminated with '\0'.
Normally fgets( ) returns line; on end of file, or
error it returns NULL.
Line Input/Output With Files
Page 263
For output, the function fputs( ) writes a string
(which need not contain a newline) to a file:
int fputs(char *line, FILE *fp)
It returns EOF if an error occurs, and non-negative
otherwise.
File Copy Program Using Line I/O
Page 264
#define MAX 81;
#include<stdio.h>
main( )
{
FILE *fp1, *fp2;
fp1 = fopen( “source.dat”, “r”);
fp2 = fopen( “target.dat”, “w”);
char string[MAX];
while ( (fgets(string, MAX, fp1)) != NULL)
{
fputs (string, fp2);
}
fclose(fp1);
fclose(fp2);
}
Formatted File Input/Output
Page 265
C facilitates data to be stored in a file in a format
of your choice. You can read and write data
from/to a file in a formatted manner using fscanf( )
and fprintf( ).
Apart from receiving as the first argument the
format specification (which governs the way data is
read/written to/from a file), these functions also
need the file pointer as a second argument.
fscanf( ) returns the value EOF upon encountering
end-of-file.
Formatted File Input/Output
Page 266
fscanf( ) assumes the field separator to be any
white space character, i.e., a space, a tab, or a
newline character.
The statement printf(“The test value is %d”, i); can
be rewritten using the function fprintf( ) as:
fprintf( stdout, “The test value is %d”, x);
The statement scanf( “%6s%d, string, &i) can be
rewritten using the function fscanf( ) as:
fscanf(stdin, “%6s%d”, string, &i);
Random Access
Page 267
Input from, or output to a file is effective relative to
a position in the file known as the current position in
the file.
For example, when a file is opened for input, the
current position in the file from which input takes
place is the beginning of the file.
If, after opening the file, the first input operation
results in ten bytes being read from the file, the
current position in the file from which the next input
operation will take place is from the eleventh byte
position.
Random Access
Page 268
It is therefore clear that input or output from a file
results in a shift in the current position in the file.
The current position in a file is the next byte position
from where data will be read from in an input
operation, or written to in an output operation.
The current position advances by the number of
bytes read or written.
A current position beyond the last byte in the file
indicates end of file.
Random Access
Page 269
For example, when a file is opened for input, the
current position in the file from which input takes
place is the beginning of the file.
If, after opening the file, the first input operation
results in ten bytes being read from the file, the
current position in the file from which the next
input operation will take place is from the eleventh
byte position.
This is sequential access, in which a file is opened,
and you start reading bytes from the file
sequentially till end of file. The same argument
cab be extended to sequential write.
Random Access
Page 270
In sharp contrast to sequential access is random
access that involves reading from any arbitrary
position in the file, or writing to any arbitrary
position in the file.
Random access therefore mandates that we must
have a mechanism for positioning the current
position in the file to any arbitrary position in the
file for performing input or output.
To facilitate this, C provides the fseek( ) function, the
prototype of which is as follows:
The fseek( ) Function
Page 271
The function fseek( ) is used for repositioning the current position in a file
opened by the function fopen( ).
int rtn = fseek(file pointer, offset, from where)
where,
int rtn is the value returned by the function fseek( ). fseek( ) returns the value 0
if successful, and 1 if unsuccessful.
FILE file-pointer is the pointer to the file
long offset is the number of bytes that the current position will shift on a file
int from-where can have one of three values:
from beginning of file (represented as 0)
from current position (represented as 1)
from end of file (represented as 2)
The fseek( ) function
Page 272
Consider the following schematic representation of a file in terms of a
string of 30 bytes. The file contains records and fields that are not
delimited by any special character.
fp = fopen(“employee.dat”, r)
employee.dat
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
current offset
Page 272
The fseek( ) function
Page 273
Consider the following schematic representation of a file in terms of a
string of 30 bytes. The file contains records and fields that are not
delimited by any special character.
fseek(fp, 10L, 0);
employee.dat
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
current offset
Page 273
The fseek( ) function
Page 274
Consider the following schematic representation of a file in terms of a
string of 30 bytes. The file contains records and fields that are not
delimited by any special character.
fgets(string, 7, fp);
employee.dat
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
current offset
Page 274
The fseek( ) function
Page 275
Consider the following schematic representation of a file in terms of a
string of 30 bytes. The file contains records and fields that are not
delimited by any special character.
fseek(fp, -10L, 2)
employee.dat
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
current offset
Page 275
The rewind( ) Function
Page 276
The function, rewind( ) is used to reposition the
current position in the file (wherever it may be) to
the beginning of the file.
The syntax of the function rewind( ) is:
rewind (file-pointer);
where file-pointer is the FILE type pointer returned by
the function fopen( ).
After invoking rewind( ), the current position in the
file is always the first byte position, i.e., at the
beginning of the file.
Updating Data in a File
Page 277
A file that needs to be updated should be opened
using fopen( ) in the “r+” mode, i.e., opening a file
for read and write.
The “r+” mode is useful for operations on files that
need to be updated.
Consider a file, “SALARY.DAT” which contains the
following structure:
Updating Data in a File
Page 278
Structure of SALARY.DAT
employee number salary
Page 278
Updating Data in a File
Page 279
/* function to read the salary field (beginning at byte no. 5 and ending at
byte 10) for each record in the file, and increase it by 100 */
#include<stdio.h>
main( )
{
FILE *fp;
char empno[5], salary[7];
double fsalary, atof( );
long int pos = 4L, offset = 4L;
/* open the file SALARY.DAT in read-write mode */
if ((fp = fopen( “SALARY.DAT”, “r+”)) = = NULL)
{
printf(“Error opening file SALARY.DAT”);
exit( );
}
Updating Data in a File
Page 280
while(( fseek( fp, offset, 1)) = = 0)
{
fgets(salary, 7, fp);
f_salary = atof(salary) + 100;
sprintf(salary, “%6.2f”, f_salary); /*convert f_salary to a string */
fseek(fp, pos, 0); /* reposition at start of salary field */
fputs(salary, fp); /* update salary field
pos += 10; /* increment pos to starting byte of salary field for the next
record */
}
printf(“The file SALARY.DAT has been updated”);
fclose(fp);
}
The ftell( ) and feof( ) Function
Page 281
The prototype declaration for the function ftell( ) is:
long ftell(FILE *fp)
ftell returns the current file position for stream, or -1
on error.
The prototype declaration for the function feof( ) is:
int feof(FILE *fp)
feof returns non-zero if the end of file indicator for
stream is set.
Page
282
Page 283
Chapter 7
User-Defined Data Types
Objectives
Page 284
In this session, you will learn to:
Trace down the genesis of user-defined data types
Declare user-defined data types, namely
Structures
Unions
Enumerations
Use pointers to structures and unions
Declare arrays of structures
Pass structures to functions
Use the typedef declaration for easier and compact coding
Use the functions fread( ) and fwrite( ) to read and write
structures to and from files
User-Defined Data Types – The
Genesis
Page 285
Consider a situation where your application needs
to read records from a file for processing.
The general approach would be to read the various
fields of a record into corresponding memory
variables.
Computations can then be performed on these
memory variables, the contents of which can then be
updated to the file.
But, this approach would involve manipulating the
current file offset for the relevant fields that need to
be updated.
User-Defined Data Types – The
Page 286
Genesis
Processing would become a lot more simpler if it
were possible to read an entire record into an
extended variable declaration, the structure of
which would be the same as the record in the file.
This extended variable declaration in turn would be
a collection of variables of different data types,
each variable matching the data type of the fields
in the record.
The flexibility with such an arrangement is that the
collection of variables making up the extended
variable declaration can be referred to in the
program using a single name.
Structures – The Definition
Page 287
A structure is a collection of one or more variables,
possibly of different types, grouped together under
a single name for convenient handling.
Structures help to organize complicated data,
particularly in large programs, because they permit
a group of related variables to be treated as a unit
instead of as separate entities.
An example of a structure is the payroll record: an
employee is described by a set of attributes such
as name, address, social security number, salary,
etc.
Structures – The Definition
Page 288
Some of these in turn could be structures: a name
has several components, as does an address.
Other examples of structures are: a point is a pair
of coordinates, a rectangle is a pair of points, and
so on.
The main change made by the ANSI standard is to
define structure assignment - structures may be
copied and assigned to, passed to functions, and
returned by functions.
Automatic structures and arrays may now also be
initialized.
Structures – Defining a Type
Page 289
When we declare a structure, we are defining a
type.
A structure declaration results in the definition of a
template or a blueprint for a user-defined data
type.
Upon declaring a structure, the compiler identifies
the structure declaration as a user-defined data
type over and above the fundamental data types,
or primitive data types built into the compiler.
A structure therefore is a mechanism for the
extension of the type mechanism in the C
language.
Declaring a Structure
Page 290
The C language provides the struct keyword for
declaring a structure. The following is a structure
declaration for employee attributes.
struct empdata
{
int empno;
char name[10];
char job[10];
float salary;
};
Declaring a Structure
Page 291
The keyword struct introduces a structure
declaration, which is a list of declarations enclosed
in braces.
An optional name called a structure tag may follow
the word struct, as with employee in the previous
example.
The tag names this kind of structure, and can be
used subsequently as a shorthand for the part of
the declaration in braces.
A struct declaration defines a type.
Declaring a Structure - Conventions
Page 292
The variables named in a structure are called members.
A structure member or tag, and an ordinary (i.e., non-
member) variable can have the same name without
conflict, since they can always be distinguished by
context.
Furthermore, the same member names may occur in
different structures, although as a matter of style one
would normally use the same names only for closely
related structure variables.
Variables of a structure type may immediately follow
the structure declaration, or may be defined
separately as follows:
Declaring a Structure Variable
Page 293
struct empdata
{
int empno;
char name[10];
char job[10];
float salary;
} emprec; /* emprec is a variable of structure type empdata */
Or a structure can be declared separately as:
struct empdata emprec;/* emprec is a variable of structure type
empdata */
Declaring a Structure
Page 294
Declaring a structure only defines a template or a blueprint for a
data type. It does not therefore result in the allocation of memory.
Memory is allocated only when a variable of a structure type is
declared in the program.
Till a variable of a structure type is created, a structure
declaration is only identified as a type, and no memory is
allocated.
Even for fundamental data types, the compiler does not allocate
memory for types. Rather, it allocates memory for implementations
of fundamental data types, in short, memory is allocated only for a
variable declaration.
Accessing Elements of a Structure
Page 295
Once a structure variable has been declared, the
individual members of the structure can be accessed
by prefixing the structure variable to the element of
the structure.
struct empdata
{
int empno;
char name[10];
char job[10];
float salary;
}
struct empdata emprec;
emprec.empno /* referring to the element of the structure variable emprec */
Passing Structures to Functions
Page 296
#include<stdio.h>
struct salesdata
{
int transaction_number;
int salesman_number;
int product_number;
int units_sold;
float value_of_sale;
};
main( )
{
struct salesdata salesvar;
Passing Structures to Functions
Page 297
printf(“enter transaction number :”);
scanf(“%d”, &salesvar.transaction_number);
fflush(stdin);
printf(“enter salesman number :”);
scanf(“%d”, &salesvar.salesman_number);
fflush(stdin);
printf(“enter product number :”);
scanf(“%d”, &salesvar.product_number);
fflush(stdin);
printf(“enter units sold :”);
scanf(“%d”, &salesvar.units_sold);
fflush(stdin);
Passing Structures to Functions
Page 298
compute(&salesvar);
.
.
.
}
compute( salesdata *salesptr)
{
static float product_unit_price = {10.0, 20.0, 30.0, 40.0};
/*product unit price for products numbered 1 through 4 */
salesptr-> value_of_sale = (float)salesptr-> units_sold *
product_unit_price[salesptr->product_number – 1]
}
Array of Structures
Page 299
Just as it is possible to declare arrays of primitive
data types, it should also be possible to declare
arrays of structures as well.
Consider the structure declaration for the employee
details used earlier.
struct empdata
{
int empno;
char name[10];
char job[10];
float salary;
};
Array of Structures
Page 300
If one were to define an array of structure
variables, one would do so as follows:
struct empdata employee_array[4];
The rationale for declaring an array of structures
becomes clear when one wants to improve I/O
efficiency in a program.
Once an array of structures is defined, it is possible
to read in a block of records from a file using an
appropriate function (fread( )), the details of which
you will see shortly.
Writing Records On To a File
Page 301
The fwrite( ) function allows a structure variable to
be written on to a file.
The following statement writes the structure variable
salesvar on to a file “SALES.DAT, which is pointed to
by the FILE type pointer fp:
fwrite( &salesvar, sizeof(struct salesdata), 1, fp);
The arguments to the function fwrite( ) are
explained as follows:
Writing Structures To a File
Page 302
Here &salesrec is the address of the structure variable to be
written to the file.
The second parameter is the size of the data to be written, i.e., size
of the structure salesdata. The parameter to the sizeof( ) operator
is the structure label, or the structure type itself, and not a variable
of the structure type. The sizeof( ) operator can be used to
determine the size of any data type in C (fundamental as well as
user-defined data types.
The third parameter of fwrite( ) is the number of structure variables
to be written to the file. In our statement, it is 1, since only one
structure variable is written to the file. In case, an array of 4
structure variables is to be written to a file using fwrite( ), the third
parameter to fwrite( ) should be 4.
The last parameter is the pointer to the file.
Reading Records from a File
Page 303
Records can be read from a file using fread( ). The
corresponding read statement using fread( ) for the earlier
fwrite( ) statement would be:
fread(&salesvar, sizeof(struct salesdata), 1, fp);
Here, the first parameter &salesvar is the address of the
structure variable salesvar into which 1 record is to be read
from the file pointed to by the FILE type pointer fp.
The second parameter specifies the size of the data to be
read into the structure variable.
Reading Records from a File
Page 304
fread( ) will return the actual number of records read from the file.
This feature can be checked for a successful read.
if ((fread( &salesvar, sizeof(struct salesdata), 1, fp)) != 1)
error code;
An odd feature of the fread( ) function is that it does not return any
special character on encountering end of file.
Therefore, after every read using fread( ), care must be taken to
check for end of file, for which the standard C library provides the
feof( ) function. It can be used thus:
if(feof(fp))
Union
Page 305
Can hold objects of different types and sizes at different times
Syntax similar to structure but meaning is different
All members of union share same storage space
Only the last data member defined can be accessed
Means of conserving memory
union declaration similar to struct declaration
eg. union u_type
{
int i;
char ch;
};
union u_type cnvt;
Unions
Page 306
In cnvt, both integer i and character ch share the
same memory location. Of course, i occupies 2 bytes
(assuming 2-byte integers, and ch uses only one
byte.
i
Byte 0 Byte 1
ch
Unions
Page 307
To access a member of a union, use the same syntax
that you would use for structures: the dot and arrow
operators.
If you are operating on the union directly, use the
dot operator. If the union is accessed through a
pointer, use the arrow operator.
For example, to assign the integer 10 to element i
of cnvt, write cnvt.i = 10;
Unions
Page 308
In the following code snippet, a pointer to cnvt is
passed to a function:
void func1( union u_type *un)
{
un->i = 10; /* assign 10 to cnvt using function */
}
Using a union can aid in the production of machine-
independent (portable) code. Because the compiler
keeps track of the actual size of the union members,
no unnecessary machine dependencies are produced.
Unions
Page 309
Unions are used frequently when specialized type
conversions are needed because you can refer to the
data held in the union in fundamentally different
ways.
Consider the problem of writing a short integer to a
disk file. The C standard library defines no function
specifically designed to write a short integer to a
file.
While you can write any type of data to a file using
fwrite( ), using fwrite( ) incurs excessive overhead for
such a simple operation.
Unions
Page 310
However, using a union, you can easily create a function called
putw( ), which represents the binary representation of a short
integer to a file one byte at a time.
The following example assumes that short integers are 2 bytes
long. First, create a union consisting of one short integer and a
2-byte character array:
union pw
{
short int i;
char ch[2];
};
Unions
Page 311
Now, you can use pw to create the version of
putw( ) shown in the following program:
#include <stdio.h>
union pw
{
short int i;
char ch[2];
};
int putw( short int num, FILE *fp);
Unions
Page 312
int main (void)
{
FILE *fp;
fp = fopen( “test.tmp”, “wb+”);
putw(1000, fp); /* write the value 1000 as an integer
*/
fclose( fp );
return 0;
}
Unions
Page 313
int putw( short int num, FILE *fp)
{
union pw word;
word.i = num;
fputc( word.ch[0], fp); /* write first half */
fputc( word.ch[1], fp); /* write second half */
}
Although putw( ) is called with a short integer, it can
still use the standard function fputc( ) to write each
byte in the integer to a disk file one byte at a time.
Enumeration
Page 314
Is a set of named integer constants that specify all
the legal values a variable of that type can have.
The keyword enum signals the start of an
enumeration type.
The general form for enumeration is
enum enum-type-name { enumeration list }
variable_list;
enum coin { penny, nickel, dime, quarter,
half_dollar, dollar};
enum coin money;
Enumeration
Page 315
Given these declarations, the following types of
statements are perfectly valid:
money = dime;
if (money = = quarter)
printf( “Money is a quarter. \n”);
The key point to understand about an enumeration
is that each of the symbols stands for an integer
value.
As such, they may be used anywhere that an integer
may be used.
Enumeration
Page 316
Each symbol is given a value one greater than the
symbol that precedes it. The value of the first
enumeration symbol is 0. Therefore,
printf( “%d %d”, penny, dime);
displays 0 2 on the screen.
You can specify the value of one or more of the
symbols by using an initializer.
Do this by following the symbol with an equal sign
and an integer value.
Enumeration
Page 317
For example, the following code assigns the value
of 100 to quarter:
enum coin { penny, nickel, dime, quarter=100,
half_dollar, dollar};
Now, the values of these symbols are:
penny 0
nickel 1
dime 2
quarter 100
half_dollar 101
dollar 102
Enumeration
Page 318
One common but erroneous assumption about
enumerations is that the symbols can be input and
output directly. This is not the case.
For example, the following code fragment will not
perform as desired:
money = dollar;
printf( “%s”, money);
Dollar is simply a name for an integer; it is not a
string.
Enumeration
Page 319
For the same reason, you cannot use this code to
achieve the desired results:
/* this code is wrong */
strcpy (money, “dime”);
That is, a string that contains the name of a symbol
is not automatically converted to that symbol.
Actually creating code to input and output
enumeration symbols is quite tedious (unless you are
willing to settle for their integer values).
Enumeration
Page 320
For example, you need the following code to display, in words, the kind of
coins that money contains:
switch (money)
{
case penny : printf( “penny”);
break;
case nickel : printf( “nickel”);
break;
case dime : printf( “dime”);
break;
case quarter : printf( “quarter”);
break;
case half_dollar : printf( “half_dollar”);
break;
case dollar : printf( “dollar”);
break;
}
Typedef Statements
Page 321
Creates synonyms (aliases) for previously defined datatypes
Used to create shorter type names
Format: typedef type new-type;
Example: typedef struct Card * CardPtr;
defines a new type name CardPtr as a synonym for type
struct Card *
typedef does not create a new datatype
◼ Only creates an alias
Page
322
Page 323
Chapter 8
Recursion
Introduction
Page 324
Many concepts typically in mathematics are defined
by presenting a process leading up to that concept.
For example, is defined as ratio of the
circumference of a circle to its diameter.
This is equivalent to the following set of instructions:
Obtain the circumference of a circle
Obtain its diameter
Divide circumference by the diameter and call the result
Clearly, the process specified must terminate with a
definite result.
The Factorial Function
Page 325
Another example of a definition specified by a
process is that of the factorial function.
Given a positive integer n, n factorial is defined as
the product of all integers between n and 1.
For example, 4 factorial equals 4 * 3 * 2 * 1 = 24.
0 factorial is defined as 1.
In mathematics, the exclamation mark (!) is used to
denote the factorial function.
The Factorial Function
Page 326
You may therefore write the definition of this
function as follows:
n! = 1 if n = = 0
n! = n * (n-1) * (n-2) * ….. * 1 if n > 0.
The dots are really a shorthand notation for all the
numbers between (n – 3) and (n – 2) multiplied
together.
To avoid the shorthand in the definition of n!, we
would have to list a formula for n! for each value of
n separately, as follows:
The Factorial Function
Page 327
0! = 1
1! = 1
2! = 2 * 1
3! = 3 * 2 * 1
4! = 4 * 3 * 2 * 1
It is cumbersome for you to list the formula for the factorial of each
integer.
To avoid any shorthand, and to avoid an infinite set of definitions,
but yet to define the function precisely, you may present an
algorithm that accepts an integer n and returns the value of n!.
The Factorial Function
Page 328
prod = 1
for (x = n; x > 0; x--)
{
prod *= x;
return (prod)
}
Such an algorithm is iterative because it calls for the
explicit repetition of some process until a certain
condition is met.
This function can be translated readily into a C
function that returns n! when n is input as a
parameter.
The Factorial Function
Page 329
Pay closer attention to the definition of n! that lists a
separate formula for each value of n.
You may note, for example, that 4! equals 4 * 3 * 2
* 1, which equals 4 * 3!.
In fact, for any n > 0, you see that n! equals n * (n
– 1)!.
Multiplying n by the product of all integers from n –
1 to 1 yields the product of all integers from n to 1.
The Factorial Function
Page 330
You may therefore define:
0! = 1
1! = 1 * 0!
2! = 2 * 1!
3! = 3 * 2!
4! = 4 * 3!
The Factorial Function
Page 331
Using the mathematical notation used earlier, you can write the
factorial of any number as:
n! = 1 if n = = 0
n! = n * (n – 1)! If n > 0
This definition is interesting since it defines the factorial function
in terms of itself.
This seems to be a circular definition and totally unacceptable
until you realize that the mathematical notation is only a concise
way of writing out the infinite number of equations necessary to
define n! for each n. 0! is defined directly as 1.
The Factorial Function
Page 332
Once 0! has been defined, defining 1! As 1* 0! is
not circular at all.
Similarly once 1! factorial has been defined,
defining 2! as 2 * 1! is equally straightforward.
It may be argued that the latter notation is more
precise than the definition of n! as n * (n-1) * (n-2)
* ….. * 1 for n > 0 because it does not resort to
dots to be filled in by the logical intuition of the
reader.
The Factorial Function
Page 333
Such a definition, which defines a problem in terms of
a simpler case of itself, is called a recursive definition.
Let us see how the recursive definition of the
factorial function may be used to evaluate 5!.
The definition states that 5! Equals 5 * 4!.
Thus, before you can evaluate 5!, you must first
evaluate 4!. Using the definition once more, you find
that 4! = 4 * 3!. Therefore, you must evaluate 3!.
The Factorial Function
Page 334
Repeating this process, you have:
5! = 5 * 4!
4! = 4 * 3!
3! = 3 * 2!
2! = 2 * 1!
1! = 1 * 0!
0! = 1
The Factorial Function
Page 335
From the aforesaid expression, it is obvious that each
case is reduced to a simpler case until we reach the
case of 0!, which is defined directly as 1.
On the last line, you have a value that is defined
directly and not as the value of another number (line
containing 0! = 1).
You may therefore backtrack from line 1 to line 6,
returning the value computed in one line to evaluate
the result of the previous line.
The Factorial Function
Page 336
This produces:
0! = 1
1! = 1 * 0! = 1 * 1 = 1
2! = 2 * 1! = 2 * 1 = 2
3! = 3 * 2! = 3 * 2 = 6
4! = 4 * 3! = 4 * 6 = 24
5! = 5 * 4! = 5 * 24 = 120
Evolving a Recursive Definition
for the Factorial
Page 337
You will now attempt to incorporate this process
into an algorithm. You want the algorithm to
accept a non-negative integer, and to compute in
a variable fact the non-negative integer that is n
factorial.
if (n == 0)
fact = 1;
else
{
x = n – 1;
find the value of x!. call it y;
fact = n * y;
}
Evolving a Recursive Definition
for the Factorial
Page 338
This algorithm exhibits the process used to compute n!
by the recursive definition.
The key to the algorithm is of course line 5 , where
you are told to find the value of x!.
This requires re-executing the algorithm with input x,
since the method for computing the factorial is the
algorithm itself.
To see that the algorithm eventually halts, note that at
the start of line 5, x equals n – 1.
Evolving a Recursive Definition
for the Factorial
Page 339
Each time the algorithm is executed, its input is one
less than the preceding time, so that 0 is eventually
input to the algorithm. At that point, the algorithm
simply returns 1.
This value is returned to line 5, which asks for the
evaluation of 0!.
The multiplication of y (which equals 1) by n (which
equals 1) is then executed and the result is returned.
This sequence of multiplications and returns continues
until the original n! has been evaluated.
Properties of Recursive Definitions
Page 340
It is important to summarize what is involved in a
recursive definition or algorithm.
One important requirement for a recursive
algorithm to be correct is that it does not generate
an infinite sequence of calls to itself.
Clearly, any algorithm that does generate such a
sequence can never terminate.
Properties of Recursive Definitions
Page 341
For at least one argument or group of arguments,
a recursive function f must be defined in terms
that do not involve f.
There must be a way out of the sequence of
recursive calls.
For example, the non-recursive portion of the n! was
0! that is equal to 1 and did not involve another
recursive definition.
C Program for a Factorial Function
Page 342
int fact(n)
int n;
{
int x, y;
if ( n == 0)
return (1);
else
{
x = n-1;
y = fact(x);
return ( n * y);
}
}
Mechanics of Recursion
Page 343
In the statement y = fact(x), the function fact( ) makes
a recursive call to itself.
This is the essential ingredient of a recursive routine.
The programmer assumes that the function being
computed has already been written, and uses it in its
own definition.
However, the programmer must ensure that this does
not lead to an endless series of calls.
Mechanics of Recursion
Page 344
It is important to examine the execution of this
function when it is called by another program.
For example, the calling program (main( )) contains
the statement: printf(“%d”, fact(4));
When the calling function calls fact( ), the parameter
n is set equal to 4. Since n is not 0, x is set equal to 3.
At that point, fact( ) is called a second time with an
argument of 3.
Mechanics of Recursion
Page 345
Therefore, the function fact( ) is reentered and the
local variables (x and y) and parameter (n) of the
block are reallocated.
Since execution has not yet left the first call of fact( ),
the first allocation of these variables remains.
Thus, there are two generations of each of these
variables in existence simultaneously in the stack for
the first call, and the second recursive call to the
function fact( ) respectively.
Mechanics of Recursion
Page 346
From any point within the second execution of fact( ),
only the most recent copy of these variables can be
referenced.
In general, each time the function fact( ) is entered
recursively, a new set of local variables and parameters
is allocated on the stack, and only this new set may be
referenced within that call of fact( ).
When a return from fact( ) to a point in a previous call
takes place, the most recent allocation of these
variables is freed from the stack when the function
returns, and the previous copy is reactivated (belonging
to the previous recursive call to the function fact( )).
Mechanics of Recursion
Page 347
This previous copy is the one that was allocated upon
the original entry to the previous call and is local to
that call.
This description suggests the use of a stack to keep
the successive generations of local variables and
parameters.
This stack is maintained by the C system and is
invisible to the programmer.
Each time that a recursive function is entered, a new
allocation of its variables is pushed on top of the
stack.
Mechanics of Recursion
Page 348
Any reference to a local variable or parameter is
through the current top of the stack.
When the function returns, the stack is popped, the
top allocation is freed, and the previous allocation
becomes the current top of the stack to be used for
referencing local variables and parameters.
You will now see how this mechanism is applied in
computing the factorial function.
Mechanics of Recursion
Page 349
The following figure contains a series of snapshots
of the stack for the variables n, x, and y as
execution of the fact( ) function proceeds.
Initially, the stacks are empty, as illustrated in (a).
After the first call on fact( ) by the calling
procedure, the situation is as shown in (b) with n
equal to 4. The variables x and y are allocated but
not initialized.
Mechanics of Recursion
Page 350
Since n does not equal 0, x is set to 3 and fact(3) is
called as shown in (c).
The new value of n does not equal 0; therefore, x is
set to 2 and fact(2) is called as shown in (d).
This continues until n equals 0 as shown in(f).
At that point, the value 1 is returned from the call to
fact(0).
Mechanics of Recursion
Page 351
Execution resumes from the point at which fact(0) was
called, which is the assignment of the returned value
to the copy of y declared in fact(1).
This is shown by the status of the stack shown in figure
(g), where the variables allocated for fact(0) have
been freed and y is set to 1.
The statement return (n * y) is then executed,
multiplying the top values of n and y to obtain 1, and
returning the value to fact(2) as shown in (h).
Mechanics of Recursion
Page 352
This process is repeated twice more, until finally the
value of y in fact(4) equals 6.
The statement return (n * y) is executed one more
time.
The product 24 is returned to the calling function
where it is printed by the statement printf(“%d”,
fact(4));
Note that each time that a recursive routine returns, it
returns to the point immediately following the point
from which it was called.
Mechanics of Recursion
Page 353
• Thus, the recursive call to fact(3) returns to the assignment
of the result to y within fact(4), but the call to fact(4)
returns to the printf( ) statement in the calling function.
(a) initially (b) fact(4) (c) fact(3) (c) fact(2)
2 * *
3 * *
3 2 *
4 * * 4 3 * 4 3 *
n x y n x y n x y n x y
Mechanics of Recursion
Page 354
(e) fact(1) (f) fact(0) (g) y = fact(0) (h) y = fact(1)
0 * *
1 * * 1 0 *
1 0 1
2 1 * 2 1 *
2 1 * 2 1 1
3 2 * 3 2 *
3 2 * 3 2 *
4 3 * 4 3 *
4 3 * 4 3 *
n x y n x y n x y n x y
Mechanics of Recursion
Page 355
(i) y = fact(2) (j) y = fact(3) Pritnf(“%d”, fact(4))
3 2 2
4 3 * 4 3 6
n x y n x y n x y
The stack at various times during execution. An asterisk indicates an uninitialized value.
Function Pointers
Page 356
Function Pointers are pointers, i.e. variables, which
point to the address of a function.
You must keep in mind, that a running program gets a
certain space in the main-memory.
Both, the executable compiled program code and the
used variables, are put inside this memory.
Thus a function in the program code is, like e.g. a
character field, nothing else than an address.
Function Pointers
Page 357
It is only important how you, or better, your
compiler/processor, interpret the memory a pointer
points to.
When you want to call a function fn() at a certain
point in your program, you just put the call of the
function fn() at that point in your source code.
Then you compile your code, and every time your
program comes to that point, your function is called.
But what can you do, if you don't know at build-time
which function has got to be called? Or, invoke
functions at runtime.
Function Pointers
Page 358
You want to select one function out of a pool of
possible functions.
However you can also solve the latter problem using
a switch-statement, where you call the functions just
like you want it, in the different branches.
But there's still another way: Use a function
pointer!
Consider the example on the following slide:
Declaring and Using Function
Page 359
Pointers
How are function pointers used? As stated above they are typically used so
one function can take in other functions as a parameter.
Consider the following example:
#include <stdio.h>
int compute( int, int (*comp)(int) );
int doubleIt( int );
int tripleIt( int );
int main()
{
int x;
x = compute( 5, &doubleIt );
printf("The result is: %i\n", x );
Declaring and Using Function
Page 360
Pointers
x = compute( 5, &tripleIt );
printf("The result is: %i\n", x );
return 0;
}
int compute( int y, int (*comp)(int) )
{ return comp( y );
}
int doubleIt( int y )
{ return y*2;
}
int tripleIt( int y )
{ return y*3;
}
Declaring and Using Function
Page 361
Pointers
Aside from main( ), this program has 3 functions.
The functions doubleIt( ) and tripleIt( ) are just run
of mill functions that take in an integer, either
double or triple the value of the argument received,
and return the value.
The function of interest here is compute( ). It starts
off normal. It returns an int, and the first parameter
is an int.
But the second parameter is pretty strange looking.
The whole "int (comp*)(int)" is one parameter,
which, in fact, is the format of a function pointer.
Declaring and Using Function
Page 362
Pointers
Basically it says compute( ) wants a pointer to a
function that takes one integer parameter, and
returns an integer. And it is going to refer to this
function through the parameter name comp.
When compute( ) is called, it uses its parameter
comp just like a normal function. When a function
pointer is passed to a function, it can be used like a
normal function.
When compute( ) is called in main( ), the second
parameter is given the name of another function
with an ampersand.
Declaring and Using Function
Page 363
Pointers
The ampersand is of course, the "address of"
operator. So this is passing the address of doubleIt(
) (and pointers are really just addresses). So this is
how compute( ) gets the pointer to doubleIt( )
It is important to note however, that if the return
type of the function doubleIt( ) and it's parameter
list did not match the ones for the function pointer
comp, it could not be used.
When you pass a function pointer, the functions
header must match the header of the function
pointer definition exactly, or it cannot be used.