Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
60 views

C Programming Through Examples

This document provides an overview of the C programming language, including its origins and evolution from B and BCPL in the early 1970s. It describes key features of C like pointers, memory allocation, recursion, and bit manipulation. It also discusses C's block structured nature, keywords, library functions, basic data types like char, int, and float, and how to define variables in C. The document uses examples like a simple "Hello World" program to illustrate basic C syntax and concepts.

Uploaded by

ksatyan.reddy
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views

C Programming Through Examples

This document provides an overview of the C programming language, including its origins and evolution from B and BCPL in the early 1970s. It describes key features of C like pointers, memory allocation, recursion, and bit manipulation. It also discusses C's block structured nature, keywords, library functions, basic data types like char, int, and float, and how to define variables in C. The document uses examples like a simple "Hello World" program to illustrate basic C syntax and concepts.

Uploaded by

ksatyan.reddy
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 363

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.

You might also like