IDS Module 1
IDS Module 1
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.
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]
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:
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
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
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.
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:
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.
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
Salesgirl[1] = 950
Salesgirl[2] = 725
Salesgirl[3] = 880
Salesgirl[4] = 940
ITEM TOTALS
Item[1] = 1185
Item[2] = 1000
Item[3] = 1310
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
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) ;
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
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.
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
.
.
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 n1 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.
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
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:
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.
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
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.
then the variable p is called apointer variable. The memory organization after
executing the above statement is shown below:
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_
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.
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.
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
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.
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
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?"
Example Z.3.2.1:Consider the following declarations and assume all are global
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.
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 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.
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.
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:
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
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");
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
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;
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.
I Garbage val ue
I
int *q; q 5002 I
Garbage value
I
I
int *r; r 5004 I Garbage value
I
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
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
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. }
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: 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
>
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
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:
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.
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:
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
+ ~
Heap area grows downwards
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:
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
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: 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
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:
I ?
p;r I-~(=~~~---,-_
I
Free memory ---------7)
~ I
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:
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:
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: .".
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.
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:
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.
void function_nameO
{
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.
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
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:
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:
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:
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.
#include <stdio.h>
#include <stdlib.h>
void mairu)
{
int *a, i, j, n;
Q Systematic Approach to Data Structures using C 2.93
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);
- calloci).
p = rnalloc(sizeof(int) * n); i
p = calloc(sizeof(int) * n);
memset(p, 0, sizeof(int) * n)
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:
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.
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:
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
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
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.
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:
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
2.9.2.4 free(ptr)
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:
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
free(ptr);
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:
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.
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.
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
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.
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
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.
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
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:
..~
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:
So, *(a+i) or *(i+a) or a[i] or i[a] are one and the same.
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.
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.
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.
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.
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.
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
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
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);
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.
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
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)
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
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.
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;
}
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.
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: bitlength;
data-type name2: bitlength;
......
......
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 2n1, 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
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