0% found this document useful (0 votes)
8 views

IDS Module 1

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

IDS Module 1

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

Chapter

8
Arrays

8.1 INTRODUCTION
An array is a group of related data items that share a common name. For instance, we can define an
array name salary to represent a set of salaries of a group of employees. A particular value is indicated
by writing a number called index number of subscript in brackets after the array name. For example,
salary[10]
represents the salary of the 10th employee. While the complete set of values is referred to as an array,
the individual values are called elements. Arrays can be of any variable type.
The ability to use a single name to represent a collection of items and to refer to an item by specifying
the item number enables us to develop concise and efficient programs. For example, a loop with the
subscript as the control variable can be used to read the entire array, perform calculations and, print out
the results. In this chapter, we shall discuss how arrays can be defined and used in C.

8.2 ONE-DIMENSIONAL ARRAYS


A list of items can be given one variable name using only one subscript and such a variable is called a
single-subscripted variable or a one-dimensional array. In mathematics, we often deal with variables
that are single-subscripted. For instance, we use the equation.
n
å xi
i =1
A=
n
to calculate the average of n values of x.
Accessing and Storing Elements

The subscripted variable xi refers to the ith element of x. In C, single-subscripted variable xi can be
expressed as
x[1], x[2], x[3] ..........x[n]
The subscript can begin with number 0. That is
x[0]
is allowed. For example, if we want to represent a set of five numbers, say (35, 40, 20, 57, 19), by an
array variable number, then we may declare the variable number as follows
int number[5];
and the computer reserves five storage locations as shown below:

number[0]
number[1]
number[2]
number[3]
number[4]

The values to the array elements can be assigned as follows:


number[0] = 35;
number[1] = 40;
number[2] = 20;
number[3] = 57;
number[4] = 19;
This would cause the array number to store the values as shown below:
number [0] 35
number [1] 40
number [2] 20
number [3] 57
number [4] 19

These elements may be used in programs just like any other C variable. For example, the following are
valid statements:
a = number[0] + 10;
number[4] = number[0] + number [2];
number[2] = x[5] + y[10];
value[6] = number[i] * 3;
The subscript of an array can be integer constants, integer variables like i, or expressions that yield
integers. C performs no bounds checking and, therefore, care should be exercised to ensure that the
array indices are within the declared limits.

Declaration of Arrays

Like any other variable, arrays must be declared before they are used. The general form of array decla-
ration is
type variable-name[size];
The type specifies the type of element that will be contained in the array, such as int, float, or char and
the size indicates the maximum number of elements that can be stored inside the array. For example,
float height[50];
declares the height to be an array containing 50 real elements. Any subscripts 0 to 49 are valid. Simi-
larly,
int group[10];
declares the group as an array to contain a maximum of 10 integer constants. Remember, any reference
to the arrays outside the declared limits would not necessarily cause an error. Rather, it might result in
unpredictable program results.
The C language treats character strings simply as arrays of characters. The size in a character string
represents the maximum number of characters that the string can hold. For instance,
char name[10];
declares the name as a character array (string) variable that can hold a maximum of 10 characters.
Suppose we read the following string constant into the string variable name.
“WELL DONE”
Each character of the string is treated as an element of the array name and is stored in the memory as
follows:
‘W’
‘E’
‘L’
‘L’
‘’
‘D’
‘O’
‘N’
‘E’
‘\O’
When the compiler sees a character string, it terminates it with an additional null character. Thus, the
element name[9] holds the null character ‘\0’ at the end. When declaring character arrays, we must
always allow one extra element space for the null terminator.
Example 8.1
Write a program using a single-subscripted variable to evaluate the following expressions:
10
Total = Â x i2
i =1
The values of x1, x2, .... are read from the terminal.
Program in Fig. 8.1 uses a one-dimensional array x to read the values and compute the sum of their
squares.

Initialization of Arrays

We can initialize the elements of arrays in the same way as the ordinary variables when they are de-
clared. The general form of initialization of arrays is:

static type array-name[size] = { list of values };

The values in the list are separated by commas. For example, the statement
static int number[3] = { 0,0,0 };
will declare the variable number as an array of size 3 and will assign zero to each element. If the
number of values in the list is less than the number of elements, then only that many elements will be
initialized. The remaining elements will be set to zero automatically. For instance,
static float total[5] = {0.0,15.75, – 10};
will initialize the first three elements to 0.0, 15.75, and –10.0 and the remaining two elements to zero.
Note that we have used the word ‘static’ before type declaration. This declares the variable as a static
variable. More about static and other class of variables are discussed in Chapter 10.
Note: Under the old version (the K & R standard), automatic arrays cannot be initialized. Only external
and static arrays may be initialized. However, the ANSI standard permits arrays with auto storage class
to be initialized. That is, the keyword static could be omitted, when an ANSI compiler is used. Since
ANSI compilers accept both the versions, we use the storage class static in our programs so that they
can run under both the K & R and ANSI compilers.
The size may be omitted. In such cases, the compiler allocates enough space for all initialized ele-
ments. For example, the statement
static int counter[ ] = {1,1,1,1};
will declare the counter array to contain four elements with initial values 1. This approach works fine
as long as we initialize every element in the array.
Character arrays may be initialized in a similar manner. Thus, the statement

static char name[ ] = {‘J’, ‘o’, ‘h’, ‘n’};

declares the name to be an array of four characters, initialized with the string “John”.
Initialization of arrays in C suffers two drawbacks.
1. There is no convenient way to initialize only selected elements.
2. There is no shortcut method for initializing a large number of array elements like the one avail-
able in FORTRAN.
Program
/ ************************************************************************************** /
/* PROGRAM SHOWING ONE-DIMENSIONAL ARRAY */
/ ************************************************************************************** /

main( )
{
int i;
float x[10], value, total;
/* .......................... READING VALUES INTO ARRAY .................. */
printf(“ENTER 10 REAL NUMBERS\n”);
for(i = 0; i < 10; i++)
{
scanf(“%f”, &value) ;
x[i] = value ;
}
/* ..........................COMPUTATION OF TOTAL ..........................*/
total = 0.0 ;
for( i = 0 ; i < 10 ; i++ )
total = total + x[i] * x[i];
/* ..........................PRINTING OF x[i] VALUES AND TOTAL ........ */
printf(“\n”);
for( i = 0 ; i < 10 ; i++ )
printf(“x[%2d] = %5.2f\n”, i+1, x[i]) ;
printf(“\ntotal = %.2f\n”, total) ;
}
Output
ENTER 10 REAL NUMBERS
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10
x[ 1] = 1.10
x[ 2] = 2.20
x[ 3] = 3.30
x[ 4] = 4.40
x[ 5] = 5.50
x[ 6] = 6.60
x[ 7] = 7.70
x[ 8] = 8.80
x[ 9] = 9.90
x[10] = 10.10
Total = 446.86

Fig. 8.1 Program to illustrate one-dimensional array

Consider an array of size, say 100. All the 100 elements have to be explicitly initialized. There is no
way to specify a repeat count. In such situations, it would be better to use a for loop to initialize the
elements. Consider the following segment of a C program:
----------
----------
for (i = 0; i < 100; i = i+1)
{
if (i < 50)
sum[i] = 0.0;
else
sum[i] = 1.0;
}
----------
----------
The first 50 elements of the array sum are initialized to zero while the remaining 50 elements are
initialized to 1.0.
Example 8.2
Given below is the list of marks obtained by a class of 50 students in an annual examination.
43 65 51 27 79 11 56 61 82 09
25 36 07 49 55 63 74 81 49 37
40 49 16 75 87 91 33 24 58 78
65 56 76 67 45 54 36 63 12 21
73 49 51 19 39 49 68 93 85 59
Write a program to count the number of students belonging to each of the following groups of marks:
0-9, 10-19, 20-29, ......., 100.
The program coded in Fig. 8.2 uses the array group containing 11 elements, one for each range of
marks. Each element counts those values falling within the range of values it represents.
For any value, we can determine the correct group element by dividing the value by 10. For example,
consider the value 59. The integer division of 59 by 10 yields 5. This is the element into which 59 is
counted.
Note that the numbers with fractional part are rounded off to the nearest integer before the integer
division occurs.

8.3 TWO-DIMENSIONAL ARRAYS


So far we have discussed the array variables that can store a list of values. There will be situations
where a table of values will have to be stored. Consider the following data table, which shows the value
of the sales of three items by four salesgirls:

Item1 Item2 Item3


Salesgirl #1 310 275 365
Salesgirl #2 210 190 325
Salesgirl #3 405 235 240
Salesgirl #4 260 300 380
The table contains a total of 12 values, three in each line. We can think of this table as a matrix
consisting of four rows and three columns. Each row represents the values of sales by a particular
salesgirl and each column represents the values of sales of a particular item.
Program
/ ************************************************************************************** /
/* PROGRAM FOR FREQUENCY COUNTING */
/ ************************************************************************************** /
#define MAXVAL 50
#define COUNTER 11
main()
{
float value[MAXVAL];
int i, low, high;
static int group[COUNTER] = { 0,0,0,0,0,0,0,0,0,0,0};

/* . . . . . . . . . . . . . . . . . . . . . .. READING AND COUNTING . . . . . . . . . . . . . . . . . . . . . .. */


for(i = 0; i < MAXVAL; i++)
{
/* .. . READING OF VALUES .. . */
scanf(“%f”, &value[i]);
/* .. . COUNTING FREQUENCY OF GROUPS .. . */
++ group[ (int) (value[i] + 0.5) / 10];
}
/* . . . . . . . . . . . . . . . . . . . . . .. PRINTING OF FREQUENCY TABLE . . . . . . . . . . . . .. */
printf(“\n”);
printf(“GROUP RANGE FREQUENCY\n\n”) ;
for( i = 0 ; i < COUNTER ; i++ )
{
low = i * 10 ;
if(i == 10)
high = 100;
else
high = low + 9 ;
printf(“ %2d %3d to %3d %d\n”,
i+1, low, high, group[i] ) ;
}
}
Output
43 65 51 27 79 11 56 61 82 09
25 36 07 49 55 63 74 81 49 37
40 49 16 75 87 91 33 24 58 78 (input data)
65 56 76 67 45 54 36 63 12 21
73 49 51 19 39 49 68 93 85 59
GROUP RANGE FREQUENCY
1 0 to 9 2
2 10 to 19 4
3 20 to 29 4
4 30 to 39 5
5 40 to 49 8
6 50 to 59 8
7 60 to 69 7
8 70 to 79 6
9 80 to 89 4
10 90 to 99 2
11 100 to 100 0

Fig. 8.2 Program for frequency counting

In mathematics, we represent a particular value in a matrix by using two subscripts such as vij. Here
v denotes the entire matrix and vij refers to the value in the ith row and jth column. For example, in the
above table v23 refers to the value 325.
C allows us to define such tables of items by using two-dimensional arrays. The table discussed
above can be defined in C as
v[4][3]
Two-dimensional arrays are declared as follows:

type array_name [row_size][column_size];

Note that unlike most other languages, which use one pair of parentheses with commas to separate array
sizes, C places each size in its own set of brackets.
Two dimensional arrays are stored in memory as shown in Fig. 8.3. As with the single-dimensional
arrays, each dimension of the array is indexed from zero to its maximum size minus one; the first index
selects the row and the second index selects the column within that row.

Column0 Column1 Column2

[0][0] [0][1] [0][2]

Row 0 310 275 365

[1][0] [1][1] [1][2]

Row 1 210 190 325

[2][0] [2][1] [2][2]

Row 2 405 235 240

[3][0] [3][1] [3][2]

Row 3 260 300 380

Fig. 8.3 Representation of a two-dimensional array in memory

Example 8.3
Write a program using a two-dimensional array to compute and print the following information from
the table of data discussed above:
(a) Total value of sales by each girl.
(b) Total value of each item sold.
(c) Grand total of sales of all items by all girls.
The program and its output are shown in Fig. 8.4. The program uses the variable value in two-
dimensions with the index i representing girls and j representing items. The following equations are used
in computing the results:
2
(a) Total sales by mth girl = Â value [m][j]
(girl_total[m])] j= 0

3
(b) Total value of nth item = Â value[i][n]
(item_total[n]) i= 0
3 2
(c) Grand total = Â Â value[i][j]
i= 0 j= 0

3
= Â girl_total[i]
i= 0

2
= Â item_total[j]
j= 0

Program
/ ************************************************************************************** /
/* PROGRAM SHOWING TWO-DIMENSIONAL ARRAYS */
/ ************************************************************************************** /
#define MAXGIRLS 4
#define MAXITEMS 3
main()
{
int value[MAXGIRLS][MAXITEMS];
int girl_total[MAXGIRLS], item_total [MAXITEMS];
int i, j, grand_total;
/* ..............READING OF VALUES AND COMPUTING girl_total ............. */
printf(“Input data\n”);
printf(“Enter values, one at a time, row-wise\n\n”);
for( i = 0 ; i < MAXGIRLS ; i++)
{
girl_total[i] = 0;
for( j = 0 ; j < MAXITEMS ; j++)
{
scanf(“%d”, &value[i][j]);
girl_total[i] = girl_total[i] + value[i][j];
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . COMPUTING item_total . . . . . . . . . . . . . . . . . . . . . . */
for( j = 0; j < MAXITEMS ; j++)
{
item_total[j] = 0;
for( i = 0 ; i < MAXGIRLS ; i++ )
item_total[j] = item_total[j] + value[i][j];
}
/* . . . . . . . . . . . . . . . . . . . . . . . . COMPUTING grand_total . . . . . . . . . . . . . . . . . . . . . . */
grand_total = 0;
for( i = 0 ; i < MAXGIRLS ; i++ )
grand_total = grand_total + girl_total[i];
/* . . . . . . . . . . . . . . . . . . . . . . . . PRINTING OF RESULTS . . . . . . . . . . . . . . . . . . . . . */
printf(“\n GIRLS TOTALS\n\n”);
for( i = 0 ; i < MAXGIRLS ; i++ )
printf{“Salesgirl[%d] = %d\n”, i+1, girl_total[i] );
printf(“\n ITEM TOTALS\n\n”);
for(j = 0 ; j < MAXITEMS ; j++ )
printf(“Item[%d] = %d\n”, j+1, item_total[j] );
printf(“\nGrand Total = %d\n”, grand_total);
}
Output
Input data
Enter values, one at a time, row_wise

310 257 365


210 190 325
405 235 240
260 300 380
GIRLS TOTALS

Salesgirl[1] = 950
Salesgirl[2] = 725
Salesgirl[3] = 880
Salesgirl[4] = 940

ITEM TOTALS
Item[1] = 1185
Item[2] = 1000
Item[3] = 1310

Grand total = 3495

Fig. 8.4 Illustration of two-dimensional arrays

Example 8.4
Write a program to compute and print a multiplication table for numbers 1 to 5 as shown below:

1 2 3 4 5

1 1 2 3 4 5

2 2 4 6 8 10

3 3 6 . . .

4 4 8 . . .

5 5 10 . . 25

The program shown in Fig. 8.5 uses a two-dimensional array to store the table values. Each value is
calculated using the control variables of the nested for loops as follows:
product [i][j ] = row * column
where i denotes rows and j denotes columns of the product table. Since the indices i and j range from 0
to 4, we have introduced the following transformation:
row = i+1
column = j+1

8.4 INITIALIZING TWO-DIMENSIONAL ARRAYS

Like the one-dimensional arrays, two-dimensional arrays may be initialized by following their declara-
tion with a list of initial values enclosed in braces. For example,
static int table[2][3] = {0,0,0,1,1,1};
initializes the elements of the first row to zero and the second row to one. The initialization is done row
by row. The above statement can be equivalently written as
static int table[2][3] = {{0,0,0}, {1,1,1}};
by surrounding the elements of each row by braces.
We can also initialize a two-dimensional array in the form of a matrix as shown below:
static int table[2][3] = {
{0,0,0,},
{1,1,1}
};
Note the syntax of the above statements. Commas are required after each brace that closes off a row,
except in the case of the last row.
If the values are missing in initializer, they are automatically set to zero. For instance, the statement

Program
/ ************************************************************************************** /
/* PROGRAM TO PRINT MULTIPLICATION TABLE */
/ ************************************************************************************** /
#define ROWS 5
#define COLUMNS 5
main()
{
int row, column, product[ROWS][COLUMNS] ;
int i, j ;
printf(“ MULTIPLICATION TABLE\n\n”) ;
printf(“ ”) ;
for( j = 1 ; j <= COLUMNS ; j++ )
printf(“%4d”, j) ;
printf(“\n”) ;
printf(“––––––––––––––––– \n”);
for( i = 0 ; j < ROWS ; i++ )
{
row = i + 1 ;
printf(“%2d || ”, row) ;

for(j = 1 ; j <= COLUMNS ; j++ )


{
column = j ;
product[i][j] = row * column ;
printf(“%4d”, product[i][j] );
}
printf(“\n”) ;
}
}
Output
MULTIPLICATION TABLE
1 2 3 4 5
–––––––––––––––––––––––
1 1 2 3 4 5
2 2 4 6 8 10
3 3 6 9 12 15
4 4 8 12 16 20
5 5 10 15 20 25

Fig. 8.5 Program to print multiplication table, using two-dimensional array

static int table[2][3] = {


{1,1},
{2}
};
will initialize the first two elements of the first row to one, the first element of the second row to two, and
all other elements to zero.
When all the elements are to be initialized to zero, the following short-cut method may be used.
static int m[3][5] = { {0}, {0} {0} };
The first element of each row is explicitly initialized to zero while other elements are automatically
initialized to zero.
Example 8.5
A survey to know the popularity of four cars (Ambassador, Fiat, Dolphin and Maruti) was conducted in
four cities (Bombay, Calcutta, Delhi and Madras). Each person surveyed was asked to give his city and
the type of car he was using. The results, in coden form, are tabulated as follows:

M 1 C 2 B 1 D 3 M 2 B 4
C 1 D 3 M 4 B 2 D 1 C 3
D 4 D 4 M 1 M 1 B 3 B 3
C 1 C 1 C 2 M 4 M 4 C 2
D 1 C 2 B 3 M 1 B 1 C 2
D 3 M 4 C 1 D 2 M 3 B 4

Codes represent the following information:


M — Madras 1 — Ambassador
D — Delhi 2 — Fiat
C — Calcutta 3 — Dolphin
B — Bombay 4 — Maruti

Write a program to produce a table showing popularity of various cars in four cities.
A two-dimensional array frequency is used as an accumulator to store the number of cars used,
under various categories in each city. For example, the element frequency [i][j] denotes the number of
cars of type j used in city i. The frequency is declared as an array of size 5 ¥ 5 and all the elements are
initialized to zero.
The program shown in Fig. 8.6 reads the city code and the car code, one set after another, from the
terminal. Tabulation ends when the letter X is read in place of a city code.

8.5 MULTIDIMENSIONAL ARRAYS

C allows arrays of three or more dimensions. The exact limit is determined by the compiler. The general
form of a multidimensional array is
type array_name[s1][s2][s3].....[sm];
where si is the size of the ith dimension. Some example are:
int survey[3][5][12];
float table[5][4][5][3];
survey is a three-dimensional array declared to contain 180 integer type elements. Similarly table is a
four-dimensional array containing 300 elements of floating-point type.
The array survey may represent a survey data of rainfall during the last three years from January to
December in five cities.

Program
/ ************************************************************************************** /
/* PROGRAM TO TABULATE SURVEY DATA */
/ ************************************************************************************** /
main()
{
int i, j, car;
static int frequency[5][5] = { {0}, {0}, {0}, {0}, {0} };
char city;
printf(“For each person, enter the city code \n”);
printf(“followed by the car code. \n”);
printf(“Enter the letter X to indicate end.\n”);
/* . . . . . . . . . . . . . . . . . . . . . . .TABULATION BEGINS . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . .. */
for(i = 1 ; i < 100 ; i++ )
{
scanf(“%c”, &city );
if( city == ‘X’ )
break;
scanf(“%d”, &car );
switch(city)
{
case ‘B’ : frequency[1][car]++;
break;
case ‘C’ : frequency[2][car]++;
break;
case ‘D’ : frequency[3][car]++;
break;
case ‘M’ : frequency[4][car]++;
break;
}
}
/* .......................... TABULATION COMPLETED AND PRINTING BEGINS ... */
printf(“\n\n”);
printf(“ POPULARITY TABLE\n\n”);
printf(“–––––––––––––––––––––––––––––––––––––––––––––––– \n”);
printf(“ City Ambassador Fiat Dolphin Maruti\n ”);
printf(“–––––––––––––––––––––––––––––––––––––––––––––––– \n”);
for(i = 1 ; i <= 4; i++)
{
switch(i)
{
case 1: printf(“Bombay ”);
break;
case 2: printf(“Calcutta ”);
break;
case 3: printf(“Delhi ”);
break;
case 4: printf(“Madras ”);
break;
}
for( j = 1 ; j <= 4; j++ )
printf(“%7d”, frequency[i][j] ) ;
printf(“\n”) ;
}
printf (“ –––––––––––––––––––––––––––––––––––––––––––––––– \n”);
/* ..........................PRINTING ENDS .................................... */
}
Output
For each person, enter the city code
followed by the car code.
Enter the letter X to indicate end.
M 1” C 2 B 1 D 3 M 2 B 4
C 1 D 3 M 4 B 2 D 1 C 3
D 4 D 4 M 1 M 1 B 3 B 3
C 1 C 1 C 2 M 4 M 4 C 2
D 1 C 2 B 3 M 1 B 1 C 2
D 3 M 4 C 1 D 2 M 3 B 4 X
POPULARITY TABLE

City Ambassador Fiat Dolphin Maruti


Bombay 2 1 3 2
Calcutta 4 5 1 0
Delhi 2 1 3 2
Madras 4 1 1 4

Fig. 8.6 Program to tabulate a survey data


If the first index denotes year, the second city and the third month, then the element survey[1][2][9]
denotes the rainfall in the month of October during the second year in city-3.
Remember that a three-dimensional array can be represented as a series of two-dimensional arrays as
shown below:
month
1 2 12
city
Year 1 1

.
.
5

month
1 2 12
city
Year 2 1

.
5

CASE STUDIES
1. Median of a List of Numbers
When all the items in a list are arranged in order, the middle value which divides the items into two parts
with equal number of items on either side is called the median. Odd number of items have just one
middle value while even number of items have two middle values. The median for even number of items
is therefore designated as the average of the two middle values.
The major steps for finding the median are as follows:
1. Read the items into an array while keeping a count of the items.
2. Sort the items in increasing order.
3. Compute median.
The program and sample output are shown in Fig. 8.7. The sorting algorithm used is as follows:
1. Compare the first two elements in the list, say a[1], and a[2]. If a[2] is smaller than a[1], then
interchange their values.
2. Compare a[2] and a[3]; interchange them if a[3] is smaller than a[2].
3. Continue this process till the last two elements are compared and interchanged.
4. Repeat the above steps n–1 times.
In repeated trips through the array, the smallest elements ‘bubble up’ to the top. Because of this
bubbling up effect, this algorithms is called bubble sorting. The bubbling effect is illustrated below for
four items.
Chapter 2: Pointers
I What are we studying in this chapter? I
• Pointer Concepts
• Pointer variables
• Accessing variables through pointers
• Pointer declaration and definition
• Initialization of pointer variables
• Pointers and functions
• Pointer to pointers
• Compatibility
• L-value and R-value
• Arrays and pointers
• Pointer arithmetic and arrays
• Passing an array to a function
• Understanding complex declarations
• Memory allocation functions
• Array of pointers
• Programming examples. - 7 hours

2.1 Introduction

Pointer is the one of the important feature available in C language. Almost all the
programs in the software industry are written only using pointers. But, many people
think that pointers concept is difficult. The correct understanding and use of pointers
is very much required for successful C programming. Pointers are one of the strongest
and also one of the most dangerous feature (if not used properly) available in C. Due
to its importance and dangerous feature, the concept of pointers is dealt in detail.
Now, let us understand the concept of pointers.

2.2 Pointer concepts

In this section, let us see "What is a pointer?" and "Explain the concept of pointer"
2.2 Q Pointers

Definition: The basic data types in C language are int, float, char, double and void.
l
Pointer is a special data type which is derived from these basic data types) So, pointer
is called derived data type. The pointer takes the values from 0 to 65535 if the size of
the RAM is 64K. The pointers are always associated with the following three
concepts:

Pointer
concept -E Pointer constants
.
Pomter values

Pointer variables

2.2.1 Pointer constants

Now, let us see "What are pointer constants?"

Definition: As we store any information in our memory, the computer stores


information in computer memory. The computer memory is divided into a number of
locations called storage cells. Each location can hold one byte of information and
each location is associated with address. Let us assume size of memory is 64K where
1K = 1024 bytes. So, total number of memory locations = 64K = 64 x 1K
= 64 x 1024 bytes
= 65536 locations

Logically, all 65536 locations in computer memory are numbered sequentially from 0
to 65535 but, physically they are divided into even bank and odd bank. Even bank is
set of memory locations with even addresses and odd bank is set of memory locations
with odd addresses as shown below:

Address Memory locations Address


o I
1
2 I 3
4 I 5
6 I 7
I
I
65530 65531
65532 I 65533
65534 I 65535
./" I <,
even bank . odd bank
Q Systematic Approach to Data Structures using C 2.3

These memory addresses are called pointer constants. We can not change them; but,
we can only use themto store data values.
For example, in the above memory organization, the addresses ranging from 0
to 65535 are pointer constants.

Note: The address of a memory location is a pointer constant and can not be changed.

2.2.2 Pointer values


Suppose, we have the following declaration:

int i = 100, j = 200, k = 300;

This declaration tells the compiler to perform the following activities:


• Reserve space for three integer values in memory
• Associate the variables i, j and k with these memory locations
• Store 100, 200 and 300 at the locations i, j and k respectively as shown below:

Variable Address Memory locations Address


0 I 1
2 I 3
4 I 5
6 I 7,
I
65530 100 65531
J 65532 2bo 65533
k 65534 300 65535
t t t t
variables address Values address

The address of the variable can not be accessed directly. The address of the variable
can be obtained using address operator (denoted by &) in C language.

Note: The address operator can be used with any variable that can be placed on the
left side of an assignment operator. Since constants, expressions and array names can
not be used on the left hand side of the assignment and hence accessing address is
invalid for constants, expressions and array names. The following are invalid:
2.4 Q Pointers

Usage Valid/Invalid Reasons for invalidity


&100 Invalid Address of a constant can not be obtained
&(p + 10) Invalid Address of an expression can not be obtained
&(p + q) Invalid Address of an expression can not be obtained
int a[10]; Address of entire array can not be obtained
&a Invalid
register a;
b Invalid Address of a register variable can not be
obtained

Note: If a is an array, then address of the first location of the array is obtained either
usmg:
&a[O] or a.

Assume that the compiler has chosen the address 65530 for the variable i, the address
65532 for the variable j and 65534 for the variable k. These addresses assigned to
variables i, j and k are called pointer values. Now, let us "Define pointer values"

Definition: Memory is divided into number of storage cells called locations. All the
locations in computer memory are numbered sequentially from 0 to 65535 (with
memory size of 64K). Out of these addresses, the system assigns some addresses of
the memory locations to the variables. These memory addresses assigned to variables
by the system are called pointer values.
For example, the addresses 65530, 65532 and 65534 which are assigned to the
variables i, j and k are pointer values. These pointer values can be obtained using
address operator (&). Using &i, &j and &k we can obtain pointer values 65530,
65532 and 65534.

Note: Without address operator (&), it is not possible to obtain the pointer value of a
variable.

Note: The pointer values (i.e., the addresses assigned to variables) may vary each
time the program is executed. That is, the address of the variable may change from
one run of the program to another.

2.2.3 Pointer variable

Now, let us see "What are pointer variables?"


Q Systematic Approach to Data Structures using c..2 5
Definition: A variable which holds address of another variable is called <pointer
variable. In other words, pointer variable is a variable which holds pointer value (i.e.,
address of a variable). For example, if i is a variable and address of i is stored in a
variable p as shown below:
p=&i;

then the variable p is called apointer variable. The memory organization after
executing the above statement is shown below:

Variable Address Memory locations


0 I
2 t
4 I
t

P 50000 651;34
I
:
65534 300
t t t
variables address Values
Physical representation Logical representation

ate that the address of variable i which is 65534 is assigned to variable p. So,
even though address of p is 50000 ( pointer value), the value stored in that location is
65534. Since p is a variable which contains address of a variable, the variable p is
called pointer variable. In the logical representation, the variable p points to the
variable i. So, we say that the variable p points to i and hence the name pointer. The
relationship between p and i is pictorially represented as shown below:

L....-__ =jr----.~1
p
__ 3-:-0_0_

If we use the statement:


p=&j;
the variable p contains the address of variable j.
Note: A pointer variable should contain only the address (i.e., pointer value).
2.6 Q Pointers

Note: Note the difference between pointer value and value stored in memory location.
Pointer value is 50000 and value stored in memory location is 65534.

2.3 Accessing variables through pointers


Once we know the concept of pointers, let us see "What are the steps to be
followed to use pointers?" The following sequence of operations have to be
performed by the programmer:

Declare a data variable Ex: int a·,


Steps to be Declare a pointer variable Ex: int *p;
followed while
using pointers Initialize a pointer variable Ex: p = &a;
Access data using pointer variable Ex: printf("%d", *p);

2.3.1 Pointer declaration and Definition

In C language, we know that all the variables should be declared before they are used.
Pointer variables also should be declared before they are used. In this section, let us
see "How to declare pointer variables?" The syntax to declare a pointer variable is
shown below: .
~
type * identifier ; .

I L~
..
Name given to the pointer variable
~ The asterisk (*) in between type and identifier
tells that the identifier is a pointer variable
'-------+ type can be any data type such as int, float,
char etc. It can be derived/user-defined data
type also.

Example 2.3.1.1: Consider the following declaration

int *p;

The above declaration can be read as ''p is pointer to integer variable" and this
declaration informs the following points:
Q Systematic Approach to Data Structures using C 2.7

• The variable p is a pointer variable. So, it should contain the address of


variable during execution.
• The type int refers to the type of the variable stored in pointer variable p
i.e., the pointer variable p should contain address of an integer variable.

Example 2.3.1.2: Consider the following declaration:

double *pd;
.
This declaration informs the compiler that pd is a pointer variable and can hold the
address of a variable of type double.

Example 2.3.1.3: In the declaration, the position of * is immaterial. For example, all
the following declarations are same:
int ' *pa;
int * pa;
int* pa;

Any of the above declaration informs that the variable pa is a pointer variable and it
should contain address of integer variable.

Example 2.3.1.4: Consider the multiple declarations as shown below:


int* pa, pb, pc;

otc: Here, most of the readers wrongly assume that the variables pa, pb and pc are
pointer variables. This is because * is attached to int. This assumption is wrong. Only
pa is a pointer variable, where as the variables pb and pc are ordinary integer
variables. For better readability, the above declaration can be written as shown below:
int *pa, pb, pc;
Now, we can easily say that pa is pointer variable because of * operator, whereas pb
and pc are integer variables and are not pointer variables. It is still better if the
variables are declared in separate lines as. shown below:
int *pa;
int . pb;
int pc;
2.8 Q Pointers

2.3.2 Dangling pointers

In the previous section, we have seen the method of declaring a pointer variable. For
example, consider the following declaration:

int *p;

This indicates that p is a pointer variable and the corresponding memory location
should contain address of an integer variable. But, the declaration will not initialize
the memory location and memory contains garbage value as shown below:

p I ~arbage value

Here, the pointer variable p does not contain a valid address and we say that it is' a
dangling pointer. Now, let us see "What is a dangling pointer?"

Deflnitione e, pointer variable should contain a valid address. A pointer variable


which does not contain a valid address is called dangling pointer.

Example Z.3.2.1:Consider the following declarations and assume all are global
variables.

int *pi; 1* Pointer to an integer *1


float *pf; 1* Pointer to a float number *1
char *pc; 1* Pointer to a character *1
~ho~ the memory organization for these variables ..

Solutiomaj] global variables are initialized by the compiler during compilation. The
pointer variables are initialized to NULL indicating they do not point to any memory
ocations as shown below:

pi
pf ~
pc
Q Systematic Approach to Data Structures using C 2.9

Example 2.3.2.2: Consider the following declarations and assume all are local
variables.

int *pi; 1* Pointer to an integer *1


float *pf; 1* Pointer to a float number *1
char *pc; 1* Pointer to a character *1

Show the memory organization for these variables.

Solution:The local variables are not initialized by the compiler during compilation.
[This is because, the local variables are created and used only during
untimelexecution time. The pointer variables also will not be initializedand hence
hey normally contain some garbage values and hence are called dangling pointers.
Irhe memory organization is shown below:

pi ~ Garbage value
pf Garbage value
pc Garbage value

Note:The pointer variables pi, pf and pc does not contain valid addresses and hence
they are dangling pointers.

Note:Most of the errors in programming are due to un-initialized pointers. These


errors are very difficult to debug. So, it is the responsibility of the programmer to
avoid dangling pointers. Hence, it is necessary to initialize the pointer variables so
that they always contain valid addresses. Only then, the pointer variables can be used
to access the data.

2.3.3 Initializing a pointer variable

Now, the question is "How to initialize a pointer variable?"


2.10 Q Pointers

- Initialization of a pointer variable is the process of assigning the 'address of a variable


to a pointer variable. The initialization of a pointer variable can be done using
following three steps:

Step. 1: Declare a data variable

Step 2: Declare a pointer variable

Step 3: Assign address of a data variable to pointer variable


using & operator and assignment operator

Note that the steps 1 and 2 can be interchanged i.e., we can first declare a pointer
variable, then declare a data variable and then initialize the pointer variable.

Example 2.3.3.1: Consider the following three statements:


int : x; /* Step 1: x is declared as a data variable */

int' *px; /* ep 2: px is declared as a poiriter variable */

px = & x; /* , _. copy address of data variable to pointer variable */

Here, the variable x is declared as integer data variable. Since px is a pointer variable
~of type integer, it should contain address of integer variable. So, using the statement:
px=&x

the valid address is stored in the pointer variable and hence the pointer variable px is
initialized.

Example 2.3.3.2: Consider the following three statements:


int x;
iot *px;
px=&x;

The above three statements can also be written as shown below:


iot x;
int *px = &x;
~ Systematic Approach to Data Structures using C 2.11

Note: It is also possible to combine the declaration of data variable, pointer variable
and initialization of pointer variable in one step as shown below:

int x, *px = &x;

Here, x is declared as a data variable, px is declared as pointer variable and px is


initialized to address of data variable x.

Example 2.3.3.3: Consider the following statements:


int p, *ip;
.float .: d, f;

ip = &p; 1* Correct initialization *1

ip = &d; 1* Wrong initialization *1

Note: First initialization is correct. In the second initialization, ip is a pointer to


integer. So, it should contain address of integer variable. But, address of floating point
variable d is stored in ip and hence it-is wrong.

Example 2.3.3.4: Pointers are flexible i.e., a pointer can point to different data
variables by storing the address of appropriate variables. Consider the following
declaration:
int x = 10, Y = 20, z = 30;
int *p;
x
p=&x;

p y
p=&y;

z
p=&z;

ote: It is observed from above example that the pointer variable p points to Jifferent
memory locations by storing the addresses of different variables. Thus, same pointe
can be pointed to different data variables.
2.12 Q Pointers

2~3.4NULL Pointer

Now, let us see"What is a NULL pointer?"

Definition: A NULL pointer is defined as a special pointer value that points to


nowhere in the memory. If it is too early in the code to assign a value to the pointer,
then it is better to assign NULL (i.e., \0 or 0) to the pointer. For example, consider the
following code:
#include <stdio.h>

int *p = NULL;

Here, the pointer variable p is a NULL pointer. This indicates that the pointer
variable p does not point to any part of the memory. The value for NULL is defined
in the header file "stdio.h". Instead of using NULL we can also use '\0' or O. The
programmer can access the data using the pointer variable p if and only it does not
contain NULL. The error condition can be checked using the following statement:

if (p = = NULL)
printf("p does not point to any memory]n");
else {
printf("Access the value of p\n");

Note: A pointer variable must be initialized. If it is too early to initialize a pointer


variable, then it is better to initialize all pointer variables to NULL in the beginning of
the code.This avoids unintentional use of an un-initialized pointer variable.

Example 2.3.4.1: Consider the following statements:

int *x;
int y;

x = y; /* Error */

Note: The value of data variable can not be assigned to a pointer variable. So, th
statementx = y; results in an error. The correct statement isx = &y.
Q Systematic Approach to Data Structures using C 2.13

2.3.5 Accessing variables through pointers


, .

Once a pointer variable is initialized with address of a variable, the question is "How
to access the value of a variable using pointer?" The value of a variable can be
accessed using pointer variable using unary operator * (called asterisk). This operator
is called indirection operator or dereferencing operator. For example, consider the
following program segment:

int x = 100, y;
int *p;

p=&x; 1* Address of x is stored in pointer variable p *1


y= *p; 1* Since p has the address of x, the value of data
1* variable x i.e, 100 is copied into y *1

Note: By specifying *p, the value of the variable whose address is stored in p can be
accessed. Here, p should be a pointer variable.

Example 2.3.5.1: The data can be accessed usmg pointers after declaration and
initialization.

Step1 : Declaration consider the following declaration:


Variables
Address

int *p; 1p 5000


t I

I Garbage val ue
I
int *q; q 5002 I
Garbage value
I
I
int *r; r 5004 I Garbage value
I

int x = 10; x 5006 ~O

Afterdeclaration, the pointer variables p, q and r does not contain valid addresses and
hence are called dangling pointers. The variable x is initialized to 10 as shown in
figure above. Once declaration is over, the next step is to initialize the pointer
variables.
2.14 Q Pointers

Step 2: Initialization Consider the following statements:

Variables

1 Adress
I

p=&x; p 5000 I
I
I
q=&x; q 5002 I
I

r=&x; r 5004 I

x 5006
I

~O
+- U
After executing these statements, the pointer variables p, q and r contains the address
of integer variable x and logical representation is shown in the figure above.

Step 3: Accessing the item 10: The item 10 can be accessed using the variables, p, q,
r and x as shown below: -

'Using p:
I
printf("&p =%u, p = %u, *p = %d\n",&p, p, *p); I /* Output */
&p = 5000, P = 5006, *p = 10
I
Using q:
I
printf("&q =%u, q = %u, *q = %d\n",&q, q, *q); I /* Output */
I &q = 5002, q = 5006, *q = 10
Using r: I
Printf("&r =%u " r = %u *r = %d\n" ,&r " r "r);,
I /* Output */
I &r = 5004, r = 5006, *r = 10

Note: Even though the variables p, q and r have different addresses, they contain
address of x only. So, different pointer variables (p, q and r in this example) may
contain address of one variable (x in this example). So, the value of x can be accessed
and changed using the variables p, q, rand x. In general) there can be multiple
pointers to a variable. .
Q 'Systematic Approach to Data Structures using C 2.15

Example 2.3.5.2: Program to add two numbers using pointers

PROGRAM TRACING
1. #include <stdio.h>
2.
3. void rnainf) Execution starts from main
sum
4.{
5.
6
int a ~ 10, b = 20, sum;
IT]
7. int *pa, *pb;
8.
9. pa = &a;
10. pb = &b;
II.
12. sum = *pa + *pb; sum = 10 + 20 =30 -.J

13.
14. Output
15. printf("Sum = %d\n", sum); Sum = 30
16. }

Explanation: Observe the following points

• In line 5 the variables a and b are initialized to 10 and 20 respectively.


• In line 7, the variables pa and pb are declared as pointer variables which are used
to store address of integer variables.

• In line 9, pa holds the address of a


• in line 10, pb holds the address of b.
• In line 12, the values of a and b are accessed using pa and pb with the help of *
operator indirectly. So, * is called indirection operator.
• In line 15 the sum of two numbers is displayed.
2.16 Q Pointers

Example 2.3.5.3: Program to read two numbers and then add two numbers using
pointers·

PROGRAM TRACING

1. #include <stdio.h>
2.
3. void mairu) Execution starts from main
4.{
5. int a, b.rsum;
6
7. int *pa, *pb;
8.
9. pa= &a; pb
10. pb = &b;
II. Input
12. scanf("%d %d",&a, &b); 10 20
13. a = 10 b = 20
14. sum = *pa + *pb; sum = 10 + 20 =30 -----~
15. Output
16. printf("Sum = %d\n", sunr); Sum = 30
17.}

Explanation After executing statement 12, the values 10 and 20 which are read from
the keyboard are copied into memory locations identified by. a and b. Then those
values are accessed using pointer variables pa and pb, added and result is stored in the
variable sum.

Note: In the statement in line 12 i.e.,


scanf("%d %d", &a, &b);
we are using &a and &b. In line 9 and 10, &a and &b are already copied into pointer
variables pa and pb. So, in place of &a and &b, we can use the pointer variables pa
and pb as shown below:
scanf("%d %d", pa, pb); 1* Since pa contains &a, pb contains &b *1

Note: there is no need of writing &pa and &pb, since pa and pb already contains the
addresses.
Q Systematic Approach to Data Structures using C 2.17
>

Example 2.3.5.4: What is the output of the following program?


PROGRAM TRACING
#include <stdio.h>
\

void main(void)
{
1* Local definitions *1
int a, b, c; aD bD cD
int *p, *q, *r; pD qD rD
1* statements *1
a~ ~ 4 I cD
a = 8, b = 4, P = &b;
pi qD rO
I
:§5
4
q = p, r = &c; a~ ~ ...
I

pi -1' ql
I
I
p = &a, *q = lO;

:ffi ~~ :ffi
*r = *p;

:ffi ~~ :ffi
*r = a + *q + *&c;
a I 8
I bl 10
I cI ,. I
26

pi I
Output
I ·ql I I rI I I
printf("%d %d %d\n",a, b, c); 8 10 26
printf("%d %d %d", *p, *q, *r); 8 10 26
}
.
2.80 Q Pointers

Example 2.8.6: Interpret the declaration int a (int b). The declaration can be
pictorially represented as shwon below:

is a function
end I ~
I int a I I (int b) I
II I...---r---i with b as integer parameter

i
start
returning
Note: If an identifier is followed by ( .... ), it indicates a function call or function
declaration. So, reading in the direction of arrow along with the labels we have:
a is a function with (int b) and returning int
i.e., a is a function which accepts b an integer as a parameter and returning an integer

2.9 Memory allocation functions

Memory can be reserved for the variables either during compilation or during
execution time (run time). This gives rise to various memory allocation techniques.
Now, let us see "What are the various memory allocation techniques?" Memory can
be allocated for variables using two different techniques:

Memory ~llocation--c Static allocation (User declarations and defmitions)


techniques Dynamic allocation (Using predefined functions)

Now, let us see "What is static memory allocation?"

Definition: If the memory is allocated (i.e., reserved) for various variables during
compilation time itself, the allocated memory space can not be expanded to
accommodate more data or can not be reduced to accommodate less data. In this
technique, once the size of the memory allocated is fixed, it can not be altered even
during execution time. This method of allocating the memory during compilation time
is called "static memory allocation". For example, consider the following declaration:

int a[lO];
Q Systematic Approach to Data Structures using C 2.81

During compilation, the compiler will allocate 10 memory locations (each location
consisting of 2 bytes say) for the variable a. In the worst case, 10 elements can be
inserted. Less than 10 elements lead to under utilization of allocated space and more
than 10 elements can not be inserted.

Disadvantages
• The memory is allocated during compilation time. Hence, the memory allocated is
fixed and can not be altered during execution time
• Leads to under utilization if more memory is allocated
• Leads to overflow if less memory is allocated
• The static nature imposes certain limitations and can find their applications only
when the data is fixed and known before processing.

Note: If there is an unpredictable storage requirement, then static allocation technique


is not at all used. This is the point where the concept of dynamic allocation comes
into picture. Now, the question is "What is dynamic memory allocation?"

Definition: Dynamic memory allocation is the process of allocating memory during


execution time (i.e., run time). This is a pretty unique feature of C when compared
with other high level languages. This allocation technique uses predefined functions
to allocate and release memory for data during execution time. So, if there is an
unpredictable storage requirement, then the dynamic allocation technique is used.
Dynamic allocation will be used commonly in the following data structures:
• dynamic arrays (discussed at the end of section 2.9.2.1)
• linked lists, trees (discussed in chapter 9 and 10)

The linked lists and trees are inherently dynamic in nature, growing and shrinking as
needed. So, to implement these data structures, the program should be able to allocate
and free memory as and when required.

Now, let us see "What are the differences between static memory allocation and
dynamic memory allocation?" The various differences between static allocation and
dynamic allocation technique are shown below:

Static allocation technique Dynamic allocation technique

1. Memory is allocated during 1. Memory is allocated during-


compilation time execution time
2.82 Q Pointers

-2. The SIze of the memory to be 2. As and when memory is required,


allocated IS fixed during memory can be allocated. If not
compilation time and can not be required, memory can be de-
altered during execution time allocated. The size of the memory
required may vary duririg execution
time.
3. Used only when the data size is 3. Used only for unpredictable
fixed and known in advance before memory requirement.
processmg

4. Execution is faster, since memory is 4. Execution is slower since memory


already allocated and data has to be allocated during run time.
manipulation IS done on these Data manipulation IS done only
allocated memory locations after allocating the memory.
\

5. Memory is allocated either in stack 5. Memory is allocated only in heap


area (for local variables) or data area
area (for global and static
variables).
6. Ex: arrays 6. Ex: Dynamic arrays, linked lists,
trees
.l

2.9.1 Memory map of C program

Before we discuss various memory allocation functions and the concept of dynamitic
memory allocation, let us see "What is the memory allocation process associated with
C program?" or "What is the memory map of C program?" A compiled C program
creates and uses the following logical distinct regions of memory:

Program memory
global/static
Memory
heap
stack

Let us assume the size of the memory is 1MB = 1024 x 1024 bytes = 1048576 bytes
of memory. All these locations are numbered sequentially from 0 to 1048575
(memory organization in C) is shown below:
Q Systematic Approach to Data Structures using C 2,83

Addresses
00000
Program Code 00001

Various functions of C Reserved for the program


(User defined functions)
(Standard functions)

Reserved for global and static


Global and static variables } variables defined in program

Heap memory } Reserved for dynamic allocation

+ ~
Heap area grows downwards

Stack area grows upwards


t t
Reserved for local variables
Stack i'04'SS'74} defined in program
(local variables) 1048575
CONCEPTUAL USE OF MEMORY

Note: Addresses are in ascending order from 0 to 1048575. The addresses for
program, data, global and static variables along with memory reserved for dynamic
allocation will be in increasing order. But, the addresses of the local variables will be
in decreasing order.

• Program memory This is the first region in the memory which is reserved for
various functions such as maim), user defined functions and standard functions.
Before execution all the necessary functions are loaded into this part of the
memory (Note: Loading is the process of copying machine code of the program
to be executed from the secondary devices into main memory).

• Global/static memory The second region is the memory where global variables
and static variables are stored. Since the lifespan of these variables is till the end
of the program, this part of the memory is reserved for these variables till the end
of the program. Note: For the detailed example, see the section 1.15.1, page 1.70.
2.84 Q Pointers

• Heap The third region is the large free memory available to the program only
during execution. This free memory available between stack area and global data
area is called heap. If memory is allocated from this area, size of heap decreases
and when memory is de-allocated the size of heap increases. In other words,
during execution size of heap changes. So, if we keep allocating the memory and
never de-allocate, there i~ possibility of "overflow" during dynamic allocation
process. In such situations, the memory allocation functions returns NULL
pointer.

• Stack This is the last area in the memory. This part of the memory holds local
variables, return address of the function calls along with arguments to functions if
any. The stack memory, heap and global memory together is called data memory:

i.e., Data memory = stack memory + heap + global/static memory


Note: In the stack area higher memory blocks are allocated for the variables defined
earlier and lower memory blocks are allocated for the variables defined later. So, all
the variables defined will have higher address values and variables defined later will
have' lower address values. For the detailed example, see the section 1.15.2, page
1.72.

2.9.2 Memory management functions


Now, let us see "What are the various memory management functions in C?" The
.1 various predefined memory management functions that are used to allocate or de-
allocate memory are shown below:
malloc 0.

Memory calloc
management
functions realloc
free

Using the functions malloct), calloct) and realloc/) additional memory space can be
allocated whereas using the function freet) unwanted space can be deleted there by
optimizing the use of storage space.

Note: If there is no space in the heap region, memory can not be allocated and this.
situation is called "Overflow of memory". In such situations, the functions malloct),
calloct) and realloct) returns NULL. It is the responsibility of the programmer to
check for overflow of memory ..
Q Systematic Approach to Data Structures using C 2.85
2.9.2.1 malloc(size)

Now, let us see "What is the purpose of using malloc?" This function allows the
program to allocate memory explicitly as and when required and the exact amount
needed during execution. This function allocates a block of memory. The size of the
. block is the number of bytes specified in the parameter. The syntax is shown below:
#include <stdlib.h> 1* Prototype definition of malloct) is available *1

ptr = (data_type *) malloc(size);

where
• ptr is a pointer variable of type data_type
• data_type can be any of the basic data type or user defined data type
• size is the number of bytes required
• On successful allocation, the function returns the address of first byte of allocated
memory. Since address is returned, the return type is a void pointer. By type
casting appropriately we can use it to store integer, float etc.
• If specified size of memory is not available, the condition is called "overflow of
memory". In such case, the function returns NULL.

Note: It is the responsibility of the programmer to check whether the sufficient


memory is allocated or not as shown below:
void function_nameO
{

ptr = (data_type *) malloc(size);


if (ptr = NULL)
{
printf("Insufficient memory\n");
exit(O);
}

Note: Many data structures, such as linked lists, trees etc., uses this function to
allocate the memory from heap area.
2.86 Q Pointers

Example 2.9.2.1.1: Now, let us see "What will happen if the following program
segment is executed?
int *ptr;
ptr = (int .*) malloc (10);

The compiler reserves the space for the variable ptr in the memory. No initialization
is done if ptr is a local variable (It is initialized to NULL if it is global variable).
Now, using the function malloct) we can request a block of memory to be reserved. A
block of memory may be allocated or may not be allocated. Now, let us discuss both
the situations.
Case 1: Un successful allocation of memory: Consider the following memory
status:
0098 0100 0102 0104 0106 0108 0110 9999

~p~~r~1 J1'ree: f
~ -7 ~(---- Used by other programs )
memory

Now, let us execute the statement:

ptr = (int *) malloc (10);


.1
Since 10 bytes of free memory space is not available, the function malloci), can not
allocate memory and hence the function returns NULL and is copied into ptr. The
memory status after execution of above statement is shown below:
0098 0100 0102 0104 0106 0108 0110 9999

Since ptr contains NULL, it indicates that memory has not been allocated and the
user should not use ptr to store some data. Instead, if ptr is NULL appropriate
message has to be displayed using the following statement:

if (ptr = NULL) )
{
printf("Insufficient memory\n"); .
return;
}
Q Systematic Approach to Data Structures using C 2.87

If ptr is not NULL, it indicates that memory has been allocated successfully and
allocated memory can be used to store the data. This situation is discussed below:

Case 2: Successful allocation of memory: Consider the following memory status:


0098 0100 0102 0104 0106 0108 0110 9999

I ?
p;r I-~(=~~~---,-_
I
Free memory ---------7)
~ I

Now, let us execute the statement:

ptr = (int *) malloc (10);

Since 10 bytes of free memory space is available, the function malloct), allocates a
block of 10 bytes of memory and returns address of the first byte. This returned
address is stored in pointer variable ptr as shown below:

0098 0100 0102 0104 0106 0108 0110 9999


I 01 00 J_~.....;.;.---t.,_...a----&.....o.,....;_r I I I
ptr }'Jk 10 bytes are reserved ) rE= .Free memory ~ r

Note: ptr points only to the first byte of memory block of 10 bytes reserved using
malloct)'
. function. Butthe allocated ·memory is not
. . .
ihi·tialized.

Now, let tis' see "How to r~ad the data into allocated memory?" Because of type cast
(int *), every two bytes are used to store an integer. So, totally 5 integers can be read
and stored in the allocated memory of 10 bytes using the following statement:

for (i = 0; i < 5; i++)


scanf("%d", ptr+i); 1* Input: 10 30 20 60 50 *1
. If we input 10, 30, 20, 60 and 50 after executing the above statement, these numbers
are stored in memory as shown below:

0102 0104 0106 0108 0110 9999


2.88 Q Pointers

Note: The expression ptr + i can be written using array notation as &p[i] or &i[p].

Now, let us see "How to access the data from allocated memory?" The contents of
the above memory locations can be accessed using *(ptr+i) with the help of de-
reference operator or using p[i] or i[p] using array notation. The equivalent
statements are: .".

for(i = 0; i < 5; i++) ~


printf("%d ", *(ptr+i) ~;

Example 2.9.2.1.2: Program showing the usage of malloct) function

#include <stdio.h> TRACING


#include <stdlib.h>
void maint) Execution starts from here
{
int" i.n;
int *ptr;
Inputs
printf("Enter the number of elements\n"); Enter the number of elements
scanf("%d",&n); 5
.1
ptr = (int *) malloc (sizeof(int)* n);

/* If sufficient memory is not allocated *,


if (ptr = NULL)
{
printf("Insuffient memory\n");
return;
}
/* Read N elements */
printf("Enter N elements\n"); Enter N elements
for (i = 0; i <n; i++)
scan f("%d ,ptr+i);
II 1020304050
printf("The given elements are\n");
for (i = 0; i < n; i++) The given elements are
printf("%d ", *(ptr+i)); 10 20 30 40 50
}
Q Systematic Approach to Data Structures using C 2.89

ow, let us see "What is dynamic array?"

Definition: A dynamic array allows us to keep the number of elements of the array
unspecified at the time of declaration. The number of elements that it can hold can be
specified during execution time. Using malloct), required number of bytes can be
allocated and operate on that memory as if it were an array using indexing. Thus, we
can have -a dynamically allocated array Later, required data can be stored in these
locations during execution time. The allocated memory can also be de-allocated if not
used.

Note: Example 2.9.2.1.2 (see previous page) shows how memory can be dynamically
allocated for an array of n elements and shows how data can be written into these
memory locations and how the data can be read from these memory locations.

2.9.2.2 calloc{n, size)

Now, let us see "What is the purpose of using calloc?" This function is used to
allocate multiple blocks of memory. Here, calloc - stands for contiguous allocation of
multiple blocks and is mainly used to allocate memory for arrays. The number of
blocks is determined by the first parameter n. The size of each block is equal to the
number of bytes specified in the parameter i.e., size. Thus, total number of bytes
allocated is n*size and all bytes will be initialized to O. The syntax is shown below:

#include <stdlib.h> /* Prototype definition of calloct) is available */

ptr = (data_type *) calloc{n, size);

where
• ptr is a pointer variable of type data_type
• data_type can be any of the basic data type or user defined data type
• n is the number of blocks to be allocated
• size is the number of bytes in each block

Now, let us see "What does this function return?" The function returns the following
values:
• On successful allocation, the function returns the address of first byte of allocated
memory. Since address is returned, the return type is a void pointer. By type
casting appropriately we can use it to store integer, float etc.
2.90 Q Pointers .

• "If specified size of memory is not available, the condition is called "overflow of
memory". In such case, the function returns NULL.

ote: It is the responsibility of the programmer to check whether the sufficient


memory is allocated or not as shown below:

void function_nameO
{

ptr = (data_type *) calloc(size);


if (ptr = NULL)
{
printf("Insufficient memory\n");
exit(O);
}

Example 2.9.2.2.1: What will happen if the following program segment' is executed?
int *ptr; . ,
ptr= (int *) calloc (5, sizeoflintj);

'Sol~iioQ: Th~.:compiler. rese~v'~s th~ space for'the variable. ptr 'In thernemory. No
initializationis done it ptr is alocal 'variable (It is initialized to NULL, if it is' global
variable) .. 'Now~ using the function calloct) we can request specified number of
blocks of memory to be reserved. The specified blocks of memory may be allocated
or may not be allocated. Now, let us discuss both the situations.

Case 1: Un successful allocation of memory: Consider the following memory


status:
0098 0100 0102 0104 0106 0108 0110 9999
? J
-p-tr--r~ F'r~e ,'~
' I' ,
:~(
__ ~_
[J=l ~ ~,
Used by other programs )
.mernory ,

Now, Let us execute the statement:


ptr = (int *) calloc (5, sizeoftintj);
~ Systematic Approach to Data Structures using C 2.91

Since5*2 = 10 bytes of free memory space is not available, the function calloct), can
notallocate memory and hence the function returns NULL and is copied into ptr. The
memorystatus after execution of above statement is shown below:
0098

ptr ,~ ~I(
0100

Free
I
memory
0102 0104

~---
0106 0108 0110

Used by other programs


9999

Since ptr contains NULL, it indicates that memory has not been allocated and the
user should not use ptr to store some data. Instead, if ptr is NULL appropriate
messagehas to be displayed using the following statement:

if (ptr = NULL)
{
printf("Insufficient memory\n");
return;
}

If ptr is not NULL, it indicates that memory has been allocated successfully and
allocatedmemory can be used to store the data. This situation is discussed below:

Case 2: Successful allocation of memory: Consider the following memory status:


0098 0100 0102 0104 0106 0108 0110 9999
I I
?
p~r r:(~:_-_- _ Free memory ----------7)
! r

ow, let us execute the statement:


ptr = (int *) calloc (5, sizeofiintj);

Since 5*2 = 10 bytes of free memory space is available, the function calloct),
allocates 5 blocks of 2 bytes each, initializes each byte to 0 and returns the address of
the first byte. This returned address is stored in pointer variable ptr as shown below:

0100 0102 0104 0106 0108 0110 9999


2.92 Q Pointers

Note: ptr points only to the first byte and the allocated memory is initialized to O's.

Now, let us see "How to read the data into allocated memory?" Because of type cast
(int *), every two bytes are used to store an integer. So, totally 5 integers can be read
and stored in the allocated memory of 10 bytes using the following statement:

for (i = 0; i < 5; i++)


scanf("%d", ptr+i); 1* Input: 10 30 20 60 50 *1

If we input 10, 30, 20, 60 and 50 after executing the above statement, these numbers
are stored in memory as shown below:
0100 0102 0104 0106 0108 0110 9999
'-------=:l~~~~~~~~ I I J
=>fE= Free memory ~ I
i ote: The expression ptr + i can be written using array notation as &p[i) or &i[p).

Now, let us see "How to access the data from allocated memory?" The contents of
the above memory locations can be accessed using *(ptr+i) with the help of de-
reference operator or using p[i) or i[p) using array notation. The equivalent
statements are: .~..

for(i=0;i<5;i++) ~
~'~~.:2~l~.~
1l~~~~~~~;,:,';'~
-.~
~o
printf("%d ", *(ptr+i) ); '''~f1;''..> " ••

The program below shows how to find maximum of n numbers which uses the
concept of dynamic arrays using calloct) function. This program prints the largest of n
elements along with its position. Note that the value of n is supplied only during
execution time. This is straightforward program and it is self-explanatory.

Example 2.9.2.2: Program to find maximum of n numbers using dynamic arrays

#include <stdio.h>
#include <stdlib.h>

void mairu)
{
int *a, i, j, n;
Q Systematic Approach to Data Structures using C 2.93

printf("Enter the no. of elements\n");


scanf("%d",&n); .

/* Allocate the required number of memory locations dynamically */


a = (int *) calloc( n, sizeof(int) );

if(a = NULL) /* If required amount of memory */


{ /* is not allocated */
printf("Insufficient memory\n");
return;
}

printf("Enter %d elements\n", n); /* Read all elements */


for ( i = 0; i < n; i++)
{
scanf("%d",&a[i]);

j = 0; /* Initial position of the largest number */

for (i = 1; i < n; i++)


{

if ( a[i] > a[j] ) j = i; /* obtain position of the largest element*/


}

printf("The biggest = %d is found in pos = %d\n",a[j], j+ 1);

/* free the memory allocated to n numbers */


free(a);

Note: In the above program &a[i] or &aU] can be replaced by a + i OF a + j. At the


same time a[i] and ajj] can be replaced by *(a + i) and *(a . j) respectively.

ow, let us see "What is the difference between malloct) and callocf)?' Even though
malloct) and callocr) are used to allocate memory, there is significant difference
between these two techniques. The difference between these two lies in the way the
memory is allocated' and initialized along with the number of parameters passed to
them.
2.94 Q Pointers

rnalloc '{',...
. --
calloc
1. The syntax of mallocr) is: 1. The syntax of calloc is:
ptr = (data_type*) malloc(size); ptr = (data_type *) calloc(n, size);

The required number of bytes to Takes two arguments: n number of


be allocated IS specifies as blocks to be allocated size IS
argument i.e., size in bytes number of bytes to be allocated for
each block.
2. Allocates a block Of memory of 2. Allocates multiple blocks of
size bytes memory, each block with the same
size
3. Allocated space will not be 3. Each byte of allocated space IS
initialized initialized to zero
4. Since no initialization takes place, 4. calloci) IS slightly more
time efficiency IS higher than computationally expensive because

- calloci).

5. This function can allocate the


of zero filling but, occasionally,
more convenient than malloct)
5. This function can allocate the
required size of memory even if required number of blocks
the memory IS not available contiguously. If required memory
contiguously but available at can not be allocated contiguously, it
returns NULL.
different locations.

6. Allocation and initialization of 6. Allocation and initialization of


allocated memory with G's can be allocated memory with O's can be
done using the following done usmg the following
statements: statements:

p = rnalloc(sizeof(int) * n); i
p = calloc(sizeof(int) * n);
memset(p, 0, sizeof(int) * n)

2.9.2.3 realloc(ptr, size)

Now, let us see"What is the purpose of using realloc?"

Before using this function, the memory should have been allocated using malloct) or
calloct). Sometimes, the allocated memory may not be sufficient and we may require
~ Systematic Approach to Data Structures using C 2.95

additional memory space. Sometimes, the allocated memory may be much larger and
we want to reduce the size of allocated memory. In both situations, the size of
allocated memory can be changed using realloci) and the process is called
reallocation of memory. The reallocation is done as shown below:
• realloct) changes the size of the block by extending or deleting the memory at
the end of the block.
• If the existing memory can be extended, ptr value will not be changed
• If the memory can not be extended, this function allocates a completely new
block and copies the contents of existing memory block into new memory
block and then deletes the old memory block. The syntax is shown below:

#include <stdlib.h> /* Prototype definition of realloc( is available */

ptr = {data_type *)realloc(ptr, size);

where
• ptr is a pointer to a block of previously allocated memory either using
malloct) or calloct).
• size is new size of the block

if (ptr == NULL)
{ /* Memory is not allocated */
printft'Tnsufficient memory\n");
return;
}

Now, let us see "What does this function return?"This function returns the following
values:

• On successful allocation, the function returns the address of first byte of allocated
memory.
• If specified size of memory can not be allocated, the condition is called "overflow
of memory". In such case, the function returns NULL.

Now, let us discuss each of the above case one by one.


2.96 ~ Pointers

Case 1: Reducing the size of the allocated memory. Consider the following
memory status
0098 0100 0102 0104 0106 0108 0110 9999

allocated by
malloct) or calloct) programs
Assume, ptr points to the address of the first byte of memory block. After executing
the statement:
ptr = (int *) realloc(ptr, 3*sizeof(int) )

only 3*2 = 6 bytes are re-allocated starting from 0100 and last block of memory
starting from 0106 is de-allocated and the resulting memory status is shown below:

0098 0100 0102 0104 0106 0108 0110 9999

l'
ptr ~re-allocated---7~ Free -7 ~ Used by other -7
by realloc memory programs
Observe from the above memory status that memory is deleted from the end of block
t ere by free memory space is increased.

Case 2: Extend the allocated memory without changing the starting address.
Consider the following memory status

0098 0100 0102 0104 0106 0108 0110 9999


0100
ptr l' ~ allocated by -7 ~ Free -7 ~ Used by other -7
malloct) or calloct) memory programs

Assume, ptr points to the address of the first byte of memory block. After executing
the statement:
ptr = (int *) realloc(ptr, 4*sizeof(int) )

only 4*2 = 8 bytes are re-allocated starting from 0100 and the resulting memory
status is shown below: .
Q Systematic Approach to Data Structures using C 2.97

0098 0100 0102 0104 0106 0108 0110 9999

Used by other -7
programs

Observe from the above memory status that free memory space is reduced by re-
allocating the extra memory.

Case 3: Extend the allocated memory by changing the starting address. Consider
the following memory status.

0098 0100 0102 0104 0106 0108 0110 9999

Used bY-7 +-<--


Free memory
other
programs
Assume, ptr points to the address of the first byte of memory block and integers 10
and 20 are stored in that memory block. After executing the statement:
ptr = (int *) realloc(ptr, 4*sizeof(int) )

4*2 = 8 bytes should be re-allocated. But, the existing memory block can not be
extended because there is no free space at the end of the current block. So, realloc
allocates a completely new block, copies the existing memory contents to the new
block and deletes the old allocation as shown below:

0098 0100 0102 0104 0106 0108 0110 9999


0100
l' Used bY-7 ~(--
ptr +- Free -7
'- memory / other
v programs
de-allocated
by realloc

After copying the data from old block to new block, the remaining locations of new
block are not initialized and hence are represented using ?? Thus, the function
realloct) guarantees that re-allocating the memory will not. destroy the original
contents of memory.
2.98 Q Pointers

Example: 2.9.2.3.1: C program showing the usage of realloct) function.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void maint)
{
char *str;
/* allocate memory for the string */
str = (char *) malloc(lO);
strcpy(str, "Computer");
str = (char *) realloc(str, 40);
strcpy(str, "Computer Science and Engineering");
}

2.9.2.4 free(ptr)

Now, let us see "What is the purpose of using freer)?':

This function is used to de-allocate (or free) the allocated block of memory which is
allocated by using the functions callocr), malloct) or realloct). It is the responsibility
.lof a programmer to de-allocate memory whenever it is not required by the program
and initialize ptr to NULL. The syntax is shown below:

#include <stdlib.h> /* Prototype definition of freer) is available */

free(ptr);
ptr = NULL;

Here, ptr is a pointer to a memory block. Now, let us see how this function works. For
example, consider the following memory configuration where ptr points to a memory
block containing 200 integers ranging from 01 to 0200.

11
ptr
Q Systematic Approach to Data Structures using C 2.99

After executing the statement:

free(ptr);

the following memory configuration is obtained.

i...--....-.il-( ---L...........--L--...;...F-L..--.-.J.-----L..----'-------i, ~
l' ree memory 7"

ptr
This shows the memory allocated for 200 integers is de-allocated and is available as
part of the free memory (heap area).Observe from the above figure that even after
freeing the memory, the pointer value stored in ptr is not changed. It still contains the
address of the first byte of the memory block allocated earlier. Using the pointer ptr
even after the memory has been released is a logical error. Note: These logical errors
are very difficult to debug and correct. So, immediately after freeing the memory, it is
better to initialize to NOLL as shown below:

/* allocate the memory */

/* de-allocate the memory */


free (ptr);
ptr=NULL;

Example: 2.9.2.4.1: Sample program to shown the problems that occur when freet) is
not used.

1. #iocIude <stdlib.h>
2.
3. void maim)
4. {
5. int *a;
6.
7. a = (int *) malloc(sizeof(iot)); a
8. *a = 100; ".
9. ......•.. ;l.

10: a = (int *) malloc(sizeof(iot));


11. *a = 200;
illQJ
12. }
2.100 ,!;l Pointers

Now, let us see "What will happen if the above program is executed?" The various
activities done during execution are shown below:
• When control enters into the function main, memory for the variable a will be
allocated and will not be initialized.
• When memory is allocated successfully by malloc (line 7), the address of the
first byte is stored in the pointer a and integer 100 is stored in the allocated
memory (line 8).
• But, when the memory is allocated successfully by using the function malloe
in line 10, address of the first byte of new memory block is copied into a
(shown using dotted lines.)

Observe that the pointer a points to the most recently allocated memory, thereby
making the earlier allocated memory inaccessible. So, memory location where the
value 100 is stored is inaccessible to any of the program and is not possible to free so
that it can be reused. This problem where in memory is reserved dynamically but no~
accessible to any of the program is called memory leakage. So, care should be taken
while allocating and de-allocating the memory. It is the responsibility of the
programmer to allocate the memory and de-allocate the memory when no longer
required.

Note: Observe the following points:


• It is an error to free memory with a NULL pointer
.l • It is an error to free a memory pointing to other than the first element of an
allocated block
• It is an error to refer to memory after it has been de-allocated

Note: Be careful, if we dynamically allocate memory in a function. We know that


local variables vanish when the function is terminated. If ptr is a pointer variable
used in a function, then the memory allocated for ptr is de-allocated automatically.
But, the space allocated dynamically using malloc, calloc or realloc will not be de-
allocated automatically when the control comes out of the function. But, the allocated
memory can not be accessed and hence can not be used. This unused un-accessible
memory results in memory leakage.

2.10 Array of pointers

In the section 2.7, we have seen how a 2-dimensional array can be represented using
pointer to an array. But, a 2-dimensional array can be expressed in-terms of array of
pointers also. The conventional array definition is

data_type array_name[expl][exp2];
Q Systematic Approach to Data Structures using C 2.51

2.5 Pointer applications


There are too many uses of pointers, But, the basic uses of pointers are shown
below:
Basic uses of pointers

L With arrays
~ in dynamic memory

There is one to one correspondence between arrays and pointers. So, there exists a
close relationship between arrays and pointers. Now, let us see the relationship
between arrays and pointers.

2.5.1 Arrays and pointers


Consider the following declaration:
int a[ 5] = {10, 20, 30, 40, 50};
r:
This declaration informs the compiler to allocate five memory locations and
initialize all memory locations with initial values as shown below:

a = 0100
Note: Assuming size of integer
&a[O] -+ O~ ie
I a[O] is 2 bytes, two bytes are reserved
&a[l] ~ 0102 (
W a[l] . for each memory location
I

&a[2] ~ 0104 3iO a[2]


I Note: The starting address of the
&a[3] ~ 0106 4b a[3] first byte of the array is called
I

&a[4] ~ 0108 s:o


I
a[4]
base address

Once the memory is allocated, the variable a contains 0100 which is the starting
address of the Oth item. This is called base address. But, the value 0100 stored in a
can not be changed. So, even though a contains an address, since its value can not
be changed, we call a as pointer constant.
Note: The array a is pointer constant only to the first element and not for the
whole array.

Note: &a[O] and a have the same pointer value 0100 and hence they are same.

a <E(~---+) &a[O] <E(~---+). (a + 9)


same same
2.52 Q Pointers

To justify above points, now let us see "What is the output of the following
program?"
PROGRAM TRACING
I a = 0100
#inc1ude <stdio.h> I
I &a[O] -+ O~ ~O
I
void maint)
l&a[l] ~ 0102 jo
{ I

int a[5] = {10, 20, 30, 40, 50}; I &a[2] ~ 0104 30


I I

40
&a[3] ~ 0106 I

I &a[4] ~ 0108 SO
I

I Output
printf("%p %p\n", &a[O], a, a+O); I 0100 0100 0100
} I
Note: We may get different answer in our computer. But, whatever it is, observe that
the value of &a[O] or a or a+O are same.

Now, let us, see "How to access the address of each element?" The address of each
item can be accessed using two different ways:
..~

address operator base address


with index with index

&a[O] i.e, 1000 (a+O) i.e., 0100


&a[l] i.e., 1002 (a+l) i.e., 0102
&a[2] i.e., 1004 (a+2) i.e., 0104
&a[3] i.e., 1006 (a+3) i.e., 0106
&a[4] i.e., 1008 (a+4) i.e., 0108

In general~ ~
&a[i] ( ) (a+i) wherei=Oto4
is same as o to 5-1
o to n - 1 (in general)
~ Systematic Approach to Data Structures using C 2.53

Note: The various ways of accessing the address of ith item in an array a is shown
below:
Normally used

&i[a] is same as i + a

So, &ali] or a+i or i+a or &i[a] are one and the same.

Once we know the address, we can obtain the data stored in that address using the
indirection operator The indirection operator * and & are inverse of each other and
hence they cancel each other. Fore example,
*(&a[O])' is same as a[O]
*(&a[4]) is same as a[4] and so on.
In general, t~a[i]) is same as a[i]
cancel each other to get a[i]
Note: The various ways of accessing the data of ithitem in an array a is shown below:

*&a[i] or a[i] is same as *(a + i) Normally used


!same same
*&i[a] or i[a] is same as *(i + a)

So, *(a+i) or *(i+a) or a[i] or i[a] are one and the same.

To justify this answer, consider the following program:

#include <stdio.h> a = 0100


I &a[O] ~ 0 100"'::tl io a[O]
void maint) I I

jo
&a[l] ~ 0 102 a[l]
{ I I

int a[5] = {10, 20, 30, 40, 50}; I &a[2] ~ 0 104 30 a[2]
int i = 3; I &a[3] ~ 0 106
I

40 a[3]
I
I &a(4) ~ 0 108 So a[4)
I
r
printfC"%d %d %d %d %d %d \n", *(&a[i)), a[iJ, *(a+i), *(i+a), i[a], *&i[a]);
}
Output
+
40
+
40
+
40
+ 40+
40
+
40
Chapter
12
Structures and Unions

12.1 INTRODUCTION

We seen that arrays can be used to represent a group of data items that belong to the same type, such as
int or float. However, if we want to represent a collection of data items of different types using a single
name, then we cannot use an array. Fortunately, C supports a constructed data type known as structure,
which is a method for packing data of different types. A structure is a convenient tool for handling a
group of logically related data items. For example, it can be used to represent a set of attributes, such as
student_name, roll_number and marks. The concept of a structure is analogous to that of a ‘record’ in
many other languages.
Structures help to organize complex data in a more meaningful way. It is a powerful concept that we
may often need to use in our program design. The chapter is devoted to the study of structures and their
applications in program development. Another related concept known as unions is also discussed.

12.2 STRUCTURE DEFINITION—DECLARING STRUCTURES

A structure definition creates a format that may be used to declare structure variables. Let us use an
example to illustrate the process of structure definition and the creation of structure variables. Consider
a book database consisting of book name, author, number of pages, and price. We can define a structure
to hold this information as follows:
struct book_bank
{
char title[20];
char author[15];
int pages;
float price;
};
The keyword struct declares a structure to hold the details of four fields, namely title, author, pages,
and price. These fields are called structure elements or members. Each member may belong to a differ-
ent type of data. book_bank is the name of the structure and is called the structure tag. The tag name
may be used subsequently to declare variables that have the tag’s structure.
Note that the above declaration has not declared any variables. It simply describes a format called
template to represent information as shown below.

The general format of a structure definition is as follows:


struct tag_name
{
data_type member1;
data_type member2;
... ...
... ...
};

We can declare structure variables using the tag name anywhere in the program. For example, the
statement
struct book_bank book1, book2, book3;
declares book1, book2, and book3 as variables of type struct book_bank.
Each one of these variables has four members as specified by the template. The complete declaration
might look like this:
struct book_bank
{
char title[20];
char author[15];
int pages;
float price;
};
struct book_bank book1, book2, book3;
Remember that the members of a structure themselves are not variables. They do not occupy any memory
until they are associated with the structure variables such as book1.
In defining a structure you may note the following syntax:
1. The template is terminated with a semicolon.
2. While the entire declaration is considered as a statement, each member is declared independently
for its name and type in a separate statement inside the template.
3. The tag name such as book_bank can be used to declare structure variables of its type, later in
the program.
It is also allowed to combine both the template declaration and variables declaration in one statement.
The declaration
struct book_bank
{
char title[20];
char author[15];
int pages;
float price;
} book1, book2, book3;
is valid. The use of tag name is optional. For example,
struct
{
....... .
....... .
....... .
} book1, book2, book3;
declares book1, book2, and book3 as structure variables representing three books, but does not include
a tag name for later use in declarations.
Normally, structure definitions appear at the beginning of the program file, before any variables or
functions are defined. They may also appear before the main, along with macro definitions, such as
#define. In such cases, the definition is global and can be used by other functions as well.

12.3 GIVING VALUES TO MEMBERS


We can assign values to the members of a structure in a number of ways. As mentioned earlier, the
members themselves are not variables. They should be linked to the structure variables in order to make
them meaningful members. For example, the word title, has no meaning where as the phrase ‘title of
book3’ has a meaning. The link between a member and a variable is established using the member
operator ‘.’ which is also known as ‘dot operator’ or ‘period operator’. For example,
book1.price
is the variable representing the price of book1 and can be treated like any other ordinary variable. Here
is how we would assign values to the members of book1:
strcpy(book1.title, “BASIC”);
strcpy(book1.author, “Balagurusamy”);
book1.pages = 250;
book1.price = 28.50;
We can also use scanf to give the values through the keyboard.
scanf(“%s\n”, book1. title);
scanf(“%d\n”, &book1.pages);
are valid input statements.
Accessing Structures
Structure definition along with the program is shown in Fig. 12.1. The scanf and printf functions illus-
trate how the member operator ‘.’ is used to link the structure members to the structure variables. The
variable name with a period and the member name is used like an ordinary variable.
Example 12.1
Define a structure type, struct personal, that would contain person name, date of joining and salary.
Using this structure, write a program to read this information for one person from the keyboard and print
the same on the screen.
Program
/**************************************************************************************/
/* DEFINING AND ASSIGNING VALUES TO STRUCTURE MEMBERS */
/**************************************************************************************/
struct personal
{
char name[20];
int day;
char month[10];
int year;
float salary;
};
main()
{
struct personal person;
printf(“Input Values\n”);
scanf(“%s %d %s %d %f”,
person.name,
&person.day,
person.month,
&person.year,
&person.salary);
printf(“%s %d %s d%, %.2f\n”,
person.name,
person.day,
person.month,
person.year,
person.salary);
}
Output
Input Values
M.L. Goel 10 January 1945 4500
M.L. Goel 10 January 1945 4500.00

Fig. 12.1 Defining and accessing structure members

12.4 STRUCTURE INITIALIZATION

Like any other data type, a structure variable can be initialized. However, a structure must be declared
as static if it is to be initialized inside a function (similar to arrays).
Note: This condition is not applicable to ANSI compilers. The ANSI standard permits initialization of
structure variables with auto storage class. We could therefore delete the word static when using ANSI
compilers. However, the use of the keyword static in the programs would enable us to run them under
both the old and ANSI standard compilers.
main( )
{
static struct
{
int weight;
float height;
}
student = {60, 180.75};
.....
.....
}
This assigns the value 60 to student.weight and 180.75 to student.height. There is a one-to-one corre-
spondence between the members and their initializing values.
A lot of variation is possible in initializing a structure. The following statements initialize two struc-
ture variables. Here, it is essential to use a tag name.
main( )
{
struct st_record
{
int weight;
float height;
};
static struct st_record student1 = {60, 180.75};
static struct st_record student2 = {53, 170.60};
.....
.....
}
Another method is to initialize a structure variable outside the function as shown below:
struct st_record /* No static word */
{
int weight,
float height;
} student1 = {60, 180.75};
main( )
{
static struct st_record student2 = {53, 170.60};
.....
.....
}
C language does not permit the initialization of individual structure members within template. The
initialization must be done only in the declaration of the actual variables.

12.5 COMPARISON OF STRUCTURE VARIABLES

Two variables of the same structure type can be compared the same way as ordinary variables. If
person1 and person2 belong to the same structure, then the following operations are valid:

Operation Meaning
person1 = person2 Assign person2 to person1.
person1 == person2 Compare all members of person1 and person2 and
return 1 if they are equal, 0 otherwise.
person1 != person2 Return 1 if all the members are not equal, 0 otherwise.

Note that not all compilers support these operations. For example, Microsoft C version does not
permit any logical operations on structure variables. In such cases, individual members can be compared
using logical operators.
Example 12.2
Write a program to illustrate the comparison of structure variables.
The program shown in Fig. 12.2 illustrates how a structure variable can be copied into another of the
same type. It also performs memberwise comparison to decide whether two structure variables are iden-
tical.

12.6 ARRAYS OF STRUCTURES

We use structures to describe the format of a number of related variables. For example, in analysing the
marks obtained by a class of students, we may use a template to describe student name and marks obtained
in various subjects and then declare all the students as structure variables. In such cases, we may declare an
array of structures, each element of the array representing a structure variable. For example,
struct class student[100];
defines an array called student, that consists of 100 elements. Each element is defined to be of the type
struct class. Consider the following declaration:
struct marks
{
int subject1;
int subject2;
int subject3;
};
main( )
{
static struct marks student[3] =
{{45,68,81}, {75,53,69}, {57,36,71}};
This declares the student as an array of three elements student[0], student[1], and student[2] and
initializes their members as follows:
student[0].subject1 = 45;
student[0].subject2 = 68;
.....
.....
student[2].subject3 = 71;

Program
/**************************************************************************************/
/* COMPARISON OF STRUCTURE VARIABLES */
/**************************************************************************************/
struct class
{
int number;
char name[20];
float marks;
};
main( )
{
int x;
static struct class student1 = {111,”RAO”,72.50};
static struct class student2 = {222,”Reddy” , 67.00};
struct class student3;
student3 = student2;
x = ((student3.number == student2.number)&&
(student3.marks == student2.marks))? 1:0;
if(x == 1)
{
printf(“\nstudent2 and student3 are same\n\n”);
printf(“%d %s %f\n”, student3.number,
student3.name,
student3.marks);
}
else
printf(“\nstudent2 and student3 are different\n\n”);
}
Output
student2 and student3 are same
222 Reddy 67.000000

Fig. 12.2 Comparing and copying structure variables

Note that the array is declared just as it would have been, with any other array. Since student is an
array, we use the usual array-accessing methods to access individual elements and then the member
operator to access members.
An array of structures is stored inside the memory in the same way as a multi-dimensional array. The
array student actually looks as shown in Fig. 12.3.
Example 12.3
For the student array discussed above, write a program to calculate the subjectwise and studentwise
totals and store them as a part of the structure.

student[0].subject1 45
. subject2 68
. subject3 81
student[1].subject1 75
. subject2 53
. subject3 69
student[2].subject1 57
. subject2 36
. subject3 71

Fig. 12.3 The array student inside memory

The program is shown in Fig. 12.4. We have declared a four-member structure, the fourth one for
keeping the student-totals. We have also declared an array total to keep the subject-totals and the grand-
total. The grand-total is given by total.total. Note that a member name can be any valid C name and can
be the same as an existing structure variable name. The linked name total.total represents the total
member of the structure variable total.

Program
/**************************************************************************************/
/* ARRAYS OF STRUCTURES */
/**************************************************************************************/
struct marks
{
int sub1;
int sub2;
int sub3;
int total;
};
main()
{
int i;
static struct marks student[3] = { {45,67,81,0},
{75,53,69,0},
{57,36,71,0}};
static struct marks total;
for(i = 0; i <= 2; i++)
{
student[i].total = student[i].sub1 +
student[i].sub2 +
student[i].sub3;
total.sub1 = total.sub1 + student[i].sub1;
total.sub2 = total.sub2 + student[i].sub2;
total.sub3 = total.sub3 + student[i].sub3;
total.total = total.total + student[i].total;
}
printf(“STUDENT TOTAL\n\n\”);
for(i = 0; i <= 2; i++)
printf(“Student[%d] %d\n”, i+1.student[i].total);
printf(“\n SUBJECT TOTAL\n\n”);

printf(“%s %d\n%s %d\n%s %d\n”,


“Subject 1 “, total.sub1,
“Subject 2 “, total.sub2,
“Subject 3 “, total.sub3);
printf(“\nGrand Total = %d\n”, total.total);
}
Output
STUDENT TOTAL
Student[1] 193
Student[2] 197
Student[3] 164
SUBJECT TOTAL
Subject 1 177
Subject 2 156
Subject 3 221

Grand Total = 554

Fig. 12.4 Illustration of subscripted structure variables

12.7 ARRAYS WITHIN STRUCTURES

C permits the use of arrays as structure members. We have already used arrays of characters inside a
structure. Similarly, we can use single- or multi-dimensional arrays of type int or float. For example,
the following structure declaration is valid:
struct marks
{
int number;
float subject[3];
} student[2];
Here, the member subject contains three elements, subject[0], subject[1] and subject[2]. These
elements can be accessed using appropriate subscripts. For example, the name
student[1].subject[2];
would refer to the marks obtained in the third subject by the second student.
Example 12.4
Rewrite the program of Example 12.3 using an array member to represent the three subjects.
The modified program is shown in Fig. 12.5. You may notice that the use of array name for subjects
has simplified the code.

12.8 STRUCTURES WITHIN STRUCTURES—NESTED STRUCTURES

Structures within a structure means nesting of structures. Nesting of structures is permitted in C. Let us
consider the following structure defined to store information about the salary of employees.
struct salary
{
char name[20];
char department[10];
int basic_pay;
int dearness_allowance;
int house_rent_allowance;
int city_allowance;
}
employee;
This structure defines name, department, basic pay and three kinds of allowances. We can group all the
items related to allowance together and declare them under a substructure as shown below:
struct salary
{
char name[2];
char department[10];
struct
{
int dearness;
int house_rent;
int city;
}
allowance;
}
employee;
The salary structure contains a member named allowance which itself is a structure with three mem-
bers. The members contained in the inner structure namely dearness, house_rent, and city can be
referred to as
employee.allowance.dearness
employee.allowance.house_rent
employee.allowance.city
An inner-most member in a nested structure can be accessed by chaining all the concerned structure
variables (from outer-most to inner-most) with the member using dot operator. The following being
invalid:
Program
/**************************************************************************************/
/* ARRAYS WITHIN A STRUCTURE */
/**************************************************************************************/
main()
{
struct marks
{
int sub[3];
int total;
};
static struct marks student[3] =
{45,67,81,0,75,53,69,0,57,36,71,0};
static struct marks total;
int i,j;
for(i = 0; i <= 2; i++)
{
for (j = 0; j < = 2; j++)
{
student[i].total += student[i].sub[j];
total sub[j] += student[i].sub[j];
}
total.total += student[i].total;
}
printf(“STUDENT TOTAL\n\n”);
for(i = 0; i <= 2; i++)
printf(“Student[%d] %d\n”, i+1.student[i].total);
printf(“\nSUBJECT TOTAL\n\n”);
for(j = 0; j <= 2; j+ +)
printf(“Subject-%d %d\n”,j+1.total.sub[j]);
printf(“\nGrand Total = %d\n”,total.total);
}

Output
STUDENT TOTAL

Student[1] 193
Student[2] 197
Student[3] 164
SUBJECT TOTAL
Subject-1 177
Subject-2 156
Subject-3 221
Grand Total = 554

Fig. 12.5 Use of subscripted members in structures

employee.allowance (actual member is missing)


employee.house_rent (inner structure variable
is missing)
An inner structure can have more than one variable. The following form of declaration is legal:
struct salary
{
.....
struct
{
int dearness;
.....
}
allowance;
arrears;
}
employee[100];
The inner structure has two variables, allowance and arrears. This implies that both of them have the same
structure template. Note the comma after the name allowance. A base member can be accessed as follows:
employee[1].allowance.dearness
employee[1].arrears.dearness
We can also use tag names to define inner structures. Example:
struct pay
{
int dearness;
int house_rent;
int city;
};
struct salary
{
char name[20];
char department[10];
struct pay allowance;
struct pay arears;
};
struct salary employee[100];
pay template is defined outside the salary template and is used to define the structure of allowance and
arrears inside the salary structure.
It is also permissible to nest more than one type of structures.
struct personal_record
{
struct name_part name;
struct addr_part address;
struct date date_of_birth;
....
....
};
struct personal_record person1;
The first member of this structure is name which is of the type struct name_part. Similarly, other
members have their structure types.
12.9 STRUCTURES AND FUNCTIONS

We know that the main philosophy of C language is the use of the functions. Therefore, it is natural that
C supports the passing of structure values as arguments to functions. There are three methods by which
the values of a structure can be transferred from one function to another.
The first method is to pass each member of the structure as an actual argument of the function call.
The actual arguments are then treated independently like ordinary variables. This is the most elementary
method and becomes unmanageable and inefficient when the structure size is large.
The second method involves passing of a copy of the entire structure to the called function. Since the
function is working on a copy of the structure, any changes to structure members within the function are
not reflected in the original structure (in the calling function). It is, therefore, necessary for the function
to return the entire structure back to the calling function. All compilers may not support this method of
passing the entire structure as a parameter.
The third approach employs a concept called pointers to pass the structure as an argument. In this
case, the address location of the structure is passed to the called function. The function can access
indirectly the entire structure and work on it. This is similar to the way arrays are passed to functions.
This method is more efficient as compared to the second one.
In this section, we discuss in detail the second method, while the third approach using pointers is
discussed in the next chapter, where pointers are dealt in detail.
The general format of sending a copy of a structure to the called function is:
function name (structure variable name)

The called function takes the following form:


data_type function name(st_name)
struct_type st_name;
{
......
......
return(expression);
}
The following points are important to note:
1. The called function must be declared for its type, appropriate to the data type it is expected to
return. For example, if it is returning a copy of the entire structure, then it must be declared as
struct with an appropriate tag name.
2. The structure variable used as the actual argument and the corresponding formal argument in the
called function must be of the same struct type.
3. The return statement is necessary only when the function is returning some data. The expression
may be any simple variable or structure variable or an expression using simple variables.
4. When a function returns a structure, it must be assigned to a structure of identical type in the
calling function.
5. The called function must be declared in the calling function for its type, if it is placed after the
calling function.
Example 12.5
Write a simple program to illustrate the method of sending an entire structure as a parameter to a
function.
A program to update an item is shown in Fig. 12.6. The function update receives a copy of the
structure variable item as one of its parameters. Note that both the function update and the formal
parameter product are declared as type struct stores. It is done so because the function uses the param-
eter product to receive the structure variable item and also to return the updated values of item.
The function mul is of type float because it returns the product of price and quantity. However, the
parameter stock, which receives the structure variable item is declared as type struct stores.
The entire structure returned by update can be copied into a structure of identical type. The statement
item = update(item,p-increment,q_increment);
replaces the old values of item by the new ones.

Program
/ ************************************************************************************** /
/* STRUCTURE AS FUNCTION PARAMETERS */
/* Passing a copy of the entire structure */
/ ************************************************************************************** /
struct stores
{
chart name[20];
float price;
int quantity,
};
main( )
{
struct stores update( );
float mul(), p_increment, value;
int q_increment;
static struct stores item = {“XYZ”, 25.75, 12};
printf(“\nInput increment values:”);
printf(“ price increment and quantity increment\n”);
scanf(“%f %d”, &p_increment, &q_increment);
/*------------------------------------------------------------------------------------------------- */
item = update(item, p_increment, q_increment);
/*------------------------------------------------------------------------------------------------ */
printf(“Update values of item\n\n”);
printf(“Name : %s\n”,item.name);
printf(“Price : %f\n”,item.price);
prinft(“Quantity : %d\n”,item.quantity);
/*------------------------------------------------------------------------------------------------*/
value = mul(item);
/*------------------------------------------------------------------------------------------------*/
printf(“\nValue of the item = %f\n”, value);
}
struct stores update(product, p, q)
struct store product;
float p;
int q;
{
product.price + = p;
product.quantity += q;
return(product);
}
float mul(stock)
struct stores stock;
{
return(stock.price * stock.quantity);
}
Output
Input increment values: price increment and quantity increment
10 12
Update values of item
Name : XYZ
Price : 35.750000
Quantity : 24
Value of the item = 858.000000

Fig. 12.6 Using structure as a function parameter


You may notice that the template of stores is defined before main(). This has made the data type
struct stores as global and has enabled the functions update and mul to make use of this definition.

12.10 UNIONS

Unions are a concept borrowed from structures and therefore follow the same syntax as structures.
However, there is major distinction between them in terms of storage. In structures, each member has its
own storage location, whereas all the members of a union use the same location. This implies that,
although a union may contain many members of different types, it can handle only one member at a time.
Like structures, a union can be declared using the keyword union as follows:
union item
{
int m;
float x;
char c;
} code;
This declares a variable code of type union item. The union contains three members, each with a
different data type. However, we can use only one of them at a time. This is due to the fact that only one
location is allocated for a union variable, irrespective of its size.

Fig. 12.7 Sharing of a storage locating by union members


The compiler allocates a piece of storage that is large enough to hold the largest variable type in the
union. In the declaration above the member x requires 4 bytes which is the largest among the members.
Figure 12.7 shows how all the three variables share the same address. This assumes that a float variable
requires 4 bytes of storage.
To access a union member, we can use the same syntax that we use for structure members. That is,
code.m
code.x
code.c
are all valid member variables. During accessing, we should make sure that we are accessing the mem-
ber whose value is currently stored. For example, the statements such as
code.m = 379;
code.x = 7859.36;
printf(“%d”, code.m);
would produce erroneous output (which is machine dependent).
In effect, a union creates a storage location that can be used by any one of its members at a time.
When a different member is assigned a new value, the new value supercedes the previous member’s
value.
Unions may be used in all places where a structure is allowed. The notation for accessing a union
member which is nested inside a structure remains the same as for the nested structures.

typedef
C provides the typedef keyword that allows you to specify a new name for a data type already provided
in the C programming language. In other words, you can use the typedef keyword to specify an identifier
for an existing data type. The declaration for the typedef keyword is:
typedef data type newname
In the above example, data type represents the existing data type for which you want to specify an
identifier and newname refers to that identifier.
As an example, consider the following declaration:
typedef int num;
Here, int is the data type available in C and a new identifier num has been specified for this data type.
The following code shows an example for using the typedef keyword:
#include <stdio.h>
#include <conio.h>
int main(void)
{
typedef int num;
num a, b, add;
a=10;
b=20;
add=0;
add = a+b;
clrscr();
printf(“The sum of %d and %d is: %d”,a,b,add);
getch();
return 0;
}

Fig. 12.8 C program for typedef


In the above code, the typedef keyword is used to specify a new name for the int data type. The output
of the above code is:
The sum of 10 and 20 is: 30

12.11 SIZE OF STRUCTURES

We normally use structures, unions, and arrays to create variables of large sizes. The actual size of these
variables in terms of bytes may change from machine to machine. We may use the unary operator sizeof
to tell us the size of a structure (or any variable). The expression
sizeof (struct x)
will evaluate the number of bytes required to hold all the members of the structure x. If y is a simple
structure variable of type struct x, then the expression
sizeof(y)
would also give the same answer. However, if y is an array variable of type struct x, then
sizeof(y)
would give the total number of bytes the array y requires.
This kind of information would be useful to determine the number of records in a database. For
example, the expression
size of(y)/sizeof(x)
would give the number of elements in the array y.

12.12 BIT FIELDS

So far, we have been using integer fields of size 16 bits to store data. There are occasions where data
items require much less than 16 bits space. In such cases, we waste memory space. Fortunately, C
permits us to use small bit fields to hold data items and thereby to pack several data items in a word of
memory. Bit fields allow direct manipulation of string of a string of preselected bits as if it represented
an integral quantity.
A bit field is a set of adjacent bits whose size can be from 1 to 16 bits in length. A word can therefore
be divided into a number of bit fields. The name and size of bit fields are defined using a structure. The
general form of bit field definition is:
struct tag-name
{
data-type name1: bit–length;
data-type name2: bit–length;
......
......
data-type nameN: bit-length;
}

The data-type is either int or unsigned int or signed int and the bit-length is the number of bits used
for the specified name. Remember that a signed bit field should have at least 2 bits (one bit for sign).
Note that the field name is followed by a colon. The bit-length is decided by the range of value to be
stored. The largest value that can be stored is 2n–1, where n is bit-length.
The internal representation of bit fields is machine dependent. That is, it depends on the size of int
and the ordering of bits. Some machines store bits from left to right and others from right to left. The
sketch below illustrates the layout of bit fields, assuming a 16-bit word that is ordered from right to left.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

name N name 2 name 1

There are several specific points to observe:


1. The first field always starts with the first bit of the word.
2. A bit field cannot overlap integer boundaries. That is, the sum of lengths of all the fields in a
structure should not be more than the size of a word. In case, it is more, the overlapping field is
automatically forced to the beginning of the next word.
3. There can be unnamed fields declared with size. Example:
Unsigned : bit-length
Such fields provide padding within the word.
4. There can be unused bits in a word.
5. We cannot take the address of a bit field variable. This means we cannot use scanf to read values
into bit fields. We can neither use pointer to access the bit fields.
6. Bit fields cannot be arrayed.
7. Bit fields should be assigned values that are within the range of their size. If we try to assign
larger values, behaviour would be unpredicted.
Suppose, we want to store and use personal information of employees in compressed form, this can be
done as follows:
struct personal
{
unsigned sex : 1
unsigned age : 7
unsigned m_status : 1
unsigned children : 3
unsigned : 4
} emp;
This defines a variable name emp with four bit fields. The range of values each field could have is
follows:
Bit field Bit length Range of value
sex 1 0 or 1
age 7 0 or 127 (27 – 1)
m_status 1 0 or 1
children 3 0 to 7 (23–1)
Once bit fields are defined, they can be referenced just as any other structure-type data item would be
referenced. The following assignment statements are valid.
emp.sex = 1;
emp.age = 50;
Remember, we cannot use scanf to read values into a bit field. We may have to read into a temporary
variable and then assign its value to the bit field. For example:
scanf(%d %d”, &AGE,&CHILDREN);
emp.age = AGE;
emp.children = CHILDREN;
One restriction in accessing bit fields is that a pointer cannot be used. However, they can be used in
normal expressions like any other variable. For example:
sum = sum + emp.age;
if(emp.m_status). . . . .;
printf(“%d\n”, emp.age);
are valid statements.
It is possible to combine normal structure elements with bit field elements. For example:
struct personal
{
char name[20]; /* normal variable */
struct addr address; /* structure variable */
unsigned sex : 1;
unsigned age : 7;
.....
.....
}
emp[100];
This declares emp as a 100 element array of type struct personal. This combines normal variable
name and structure type variable address with bit fields.
Bit fields are packed into words as they appear in the definition. Consider the following definition.
struct pack
{
unsigned a:2;
int count;
unsigned b : 3;
};
Here, the bit field a will be in one word, the variable count will be in the second word and the bit field
b will be in the third word. The fields a and b would not get packed into the same word.

CASE STUDY
Book Shop Inventory

A book shop uses a personal computer to maintain the inventory of books that are being sold at the shop.
The list includes details such as author, title, price, publisher, stock position, etc. Whenever a customer
wants a book, the shopkeeper inputs the title and author of the book and the system replies whether it is

You might also like