0% found this document useful (0 votes)
19 views23 pages

Advanced Concepts of C and Introduction To Data Structures

This document provides an overview of the topics that will be covered in the session on advanced concepts of C and introduction to data structures, including structures, arrays, pointers, advantages and disadvantages of pointers, usage of pointers, difference between arrays and pointers, passing parameters by value, reference and address, dynamic allocation and deallocation of memory, dynamic arrays, scope and lifetime of variables, unions, enumerations, introduction to arrays, pointers, multidimensional arrays, pointer arithmetic, and initialization of arrays and multidimensional arrays.

Uploaded by

Bibekananda Shi
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
Download as doc, pdf, or txt
0% found this document useful (0 votes)
19 views23 pages

Advanced Concepts of C and Introduction To Data Structures

This document provides an overview of the topics that will be covered in the session on advanced concepts of C and introduction to data structures, including structures, arrays, pointers, advantages and disadvantages of pointers, usage of pointers, difference between arrays and pointers, passing parameters by value, reference and address, dynamic allocation and deallocation of memory, dynamic arrays, scope and lifetime of variables, unions, enumerations, introduction to arrays, pointers, multidimensional arrays, pointer arithmetic, and initialization of arrays and multidimensional arrays.

Uploaded by

Bibekananda Shi
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1/ 23

Session 1

Advanced Concepts of C and


Introduction to data structures

During this session you will learn about:

 Structures.
 Arrays.
 Pointers.
 Advantages and disadvantages of a pointer.
 Usage of pointers.
 Difference between arrays and pointers.
 Passing parameters – by value, by reference and by
address.
 Dynamic allocation and de-allocation of memory.
 Dynamic arrays.
 Scope and lifetime of a variable.
 Unions.
 Enumerations.

1.1. Introduction

This chapter familiarizes you with the concepts of


arrays, pointers and dynamic memory allocation and de-
allocation techniques. We briefly discuss about types of data
structures and algorithms. Let us start the discussion with
data types.

1.2. Data Types

As we know that the data, which will be given to us,


should be stored and again referred back. These are done with
the help of variables. A particular variable’s memory
requirement depends on which type it belongs to. The different
types in C are integers, float (Real numbers), characters,
double, long, short etc. These are the available built in
types.

Many a times we may come across many data members of same


type that are related. Giving them different variable names

1
Data Structures with ‘c’
and later remembering them is a tedious process. It would be
easier for us if we could give a single name as a parent name
to refer to all the identifiers of the same type. A particular
value is referred using an index, which indicates whether the
value is first, second or tenth in that parents name.

We have decided to use the index for reference as the


values occupy successive memory locations. We will simply
remember one name (starting address) and then can refer to any
value, using index. Such a facility is known as ARRAYS.

1.3. Arrays

An array can be defined as the collection of the


sequential memory locations, which can be referred to by a
single name along with a number, known as the index, to access
a particular field or data.

When we declare an array, we will have to assign a type


as well as size.

e.g.

When we want to store 10 integer values, then we can use


the following declaration.

int A[10];

By this declaration we are declaring A to be an array, which


is supposed to contain in all 10 integer values.

When the allocation is done for array a then in all 10


locations of size 2 bytes for each integer i.e. 20 bytes will
be allocated and the starting address is stored in A.

When we say A[0] we are referring to the first integer


value in A.

| ----------------- Array----------------|

A[0]A[1] A[9]

fig(1). Array representation

Hence if we refer to the i th value in array we should


write A[i-1]. When we declare the array of SIZE elements,

2
Data Structures with ‘c’
where, SIZE is given by the user, the index value changes from
0 to SIZE-1.

Here it should be remembered that the size of the array


is ‘always a constant’ and not a variable. This is because the
fixed amount of memory will be allocated to the array before
execution of the program. This method of allocating the memory
is known as ‘static allocation’ of memory.

1.4. Handling Arrays

Normally following procedure is followed for programming


so that, by changing only one #define statement we can run
the program for arrays of different sizes.

#define SIZE 10

int a[SIZE], b[SIZE];

Now if we want this program to run for an array of 200


elements we need to change just the #define statement.

1.5. Initializing the Arrays.

One method of initializing the array members is by using


the ‘for’ loop. The following for loop initializes 10 elements
with the value of their index.

#define SIZE 10

main()
{
int arr[SIZE], i;

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


{
arr[i] = i;
}
}

An array can also be initialized directly as follows.

int arr[3] = {0,1,2};

An explicitly initialized array need not specify size but


if specified the number of elements provided must not exceed
the size. If the size is given and some elements are not
explicitly initialized they are set to zero.

3
Data Structures with ‘c’
e.g.

int arr[] = {0,1,2};


int arr1[5] = {0,1,2}; /* Initialized as {0,1,2,0,0}*/
const char a_arr3[6] = ”Daniel”; /* ERROR; Daniel has 7
elements 6 in Daniel and a \0*/

To copy one array to another each element has to be


copied using for structure.

Any expression that evaluates into an integral value can


be used as an index into array.

e.g.

arr[get_value()] = somevalue;

1.6. Multidimensional Arrays

An array in which the elements need to be referred by


two indices it is called a two-dimensional array or a “matrix”
and if it is referred using more than two indices, it will be
Multidimensional Array.

e.g.

int arr[4][3];

This is a two-dimensional array with 4 as row dimension


and 3 as a column dimension.

1.7. Initialization of Two Dimensional Array

Just like one-dimensional arrays, members of matrices can


also be initialized in two ways – using ‘for’ loop and
directly. Initialization using nested loops is shown below.

e.g.

int arr[10][10];
for(int i = 1;i< = 10;i++)
for(int j = 1; j< = 10;j++)
{
arr[i][j] = i+j;
}

4
Data Structures with ‘c’
Now let us see how members of matrices are initialized
directly.

e.g.
int arr[4][3] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11}};

The nested brackets are optional.

1.8. Pointers

The computer memory is a collection of storage cells.


These locations are numbered sequentially and are called
addresses.

Pointers are addresses of memory location. Any


variable, which contains an address is a pointer
variable.

Pointer variables store the address of an object,


allowing for the indirect manipulation of that object.
They are used in creation and management of objects that
are dynamically created during program execution.

1.9. Advantages and disadvantages of pointers

Pointers are very effective when

- The data in one function can be modified by other


function by passing the address.
- Memory has to be allocated while running the
program and released back if it is not required
thereafter.
- Data can be accessed faster because of direct
addressing.

The only disadvantage of pointers is, if not understood


and not used properly can introduce bugs in the program.

1.10. Declaring and initializing pointers

Pointers are declared using the (*) operator. The


general format is:

data_type *ptrname;

5
Data Structures with ‘c’
where type can be any of the basic data type such as
integer, float etc., or any of the user-defined data
type. Pointer name becomes the pointer of that data type.

e.g.

int *iptr;
char *cptr;
float *fptr;

The pointer iptr stores the address of an integer.


In other words it points to an integer, cptr to a
character and fptr to a float value.

Once the pointer variable is declared it can be made


to point to a variable with the help of an address
(reference) operator(&).

e.g.

int num = 1024;


int *iptr;
iptr = &num; // iptr points to the variable num.

The pointer can hold the value of 0(NULL), indicating


that it points to no object at present. Pointers can never
store a non-address value.

e.g.
iptr1=ival; // invalid, ival is not address.

A pointer of one type cannot be assigned the address


value of the object of another type.
e.g.
double dval, *dptr = &dval; // allowed
iptr = &dval ; //not allowed

1.11.Pointer Arithmetic

The pointer values stored in a pointer variable can be


altered using arithmetic operators. You can increment or
decrement pointers, you can subtract one pointer from another,
you can add or subtract integers to pointers but two pointers
can not be added as it may lead to an address that is not
present in the memory. No other arithmetic operations are

6
Data Structures with ‘c’
allowed on pointers than the ones discussed here. Consider a
program to demonstrate the pointer arithmetic.
e.g.
# include<stdio.h>
main()
{
int a[]={10,20,30,40,50}; ptr--> F000 10
int *ptr; F002 20
int i; F004 30
ptr=a; F006 40
for(i=0; i<5; i++) F008 50
{
printf(“%d”,*ptr++);
}
}

Output:
10 20 30 40 50

The addresses of each memory location for the array ‘a’


are shown starting from F002 to F008. Initial address of F000
is assigned to ‘ptr’. Then by incrementing the pointer value
next values are obtained. Here each increment statement
increments the pointer variable by 2 bytes because the size of
the integer is 2 bytes. The size of the various data types is
shown below for a 16-bit machine. It may vary from system to
system.

char 1byte
int 2bytes
float 4bytes
long int 4bytes
double 8bytes
short int 2bytes

1.12.Array of pointers

Consider the declaration shown below:

char *A[3]={“a”, “b”, “Text Book”};

7
Data Structures with ‘c’
The example declares ‘A’ as an array of character
pointers. Each location in the array points to string of
characters of varying length. Here A[0] points to the first
character of the first string and A[1] points to the first
character of the second string, both of which contain only one
character. however, A[2] points to the first character of the
third string, which contains 9 characters.

1.13.Passing parameters to the functions

The different ways of passing parameters into the


function are:
- Pass by value( call by value)
- Pass by address/pointer(call by address)

In pass by value we copy the actual argument into the


formal argument declared in the function definition. Therefore
any changes made to the formal arguments are not returned back
to the calling program.

In pass by address we use pointer variables as arguments.


Pointer variables are particularly useful when passing to
functions. The changes made in the called functions are
reflected back to the calling function. The program uses the
classic problem in programming, swapping the values of two
variables.

void val_swap(int x, int y) // Call by Value


{
int t;

t = x;
x = y;
y = t;
}

void add_swap(int *x, int *y) // Call by Address


{
int t;

t = *x;
*x = *y;
*y = t;
}

void main()
{

8
Data Structures with ‘c’
int n1 = 25, n2 = 50;

printf(“\n Before call by Value : ”);


printf(“\n n1 = %d n2 = %d”,n1,n2);
val_swap( n1, n2 );
printf(“\n After call by value : ”);
printf(“\n n1 = %d n2 = %d”,n1,n2);

printf(“\n Before call by Address : ”);


printf(“\n n1 = %d n2 = %d”,n1,n2);
val_swap( &n1, &n2 );
printf(“\n After call by value : ”);
printf(“\n n1 = %d n2 = %d”,n1,n2);

Output:

Before call by value : n1 = 25 n2 = 50


After call by value : n1 = 25 n2 = 50 // x = 50, y =
25

Before call by address : n1 = 25 n2 = 50


After call by address : n1 = 50 n2 = 25 //x = 50, y =
25

1.14. Relation between Pointers and Arrays

Pointers and Arrays are related to each other. All


programs written with arrays can also be written with the
pointers. Consider the following:

int arr[] = {0,1,2,3,4,5,6,7,8,9};

To access the value we can write,

arr[0] or *arr;
arr[1] or *(arr+1);

Since ‘*’ is used as pointer operator and is also used to


dereference the pointer variables, you have to know the
difference between them thoroughly.

*(arr+1) means the address of arr is increased by 1 and


then the contents are fetched.
*arr+1 means the contents are fetched from address arr
and one is added to the content.

9
Data Structures with ‘c’
Now we have understood the relation between an array and
pointer. The traversal of an array can be made either through
subscripting or by direct pointer manipulation.

e.g.

void print(int *arr_beg, int *arr_end)


{
while(arr_beg ! = arr_end)
{
printf(“%i”,*arr);
++arr_beg;
}
}

void main()
{
int arr[] = {0,1,2,3,4,5,6,7,8,9}
print(arr,arr+9);
}

arr_end initializes element past the end of the array so


that we can iterate through all the elements of the array.
This however works only with pointers to array containing
integers.

1.15. Scope Rules and Storage Classes

Since we explained that the values in formal variables


are not reflected back to the calling program, it becomes
important to understand the scope and lifetime of the
variables.

The storage class determines the life of a variable in


terms of its duration or its scope. There are four storage
classes:

- automatic
- static
- external
- register

1.15.1 Automatic Variables

Automatic variables are defined within the functions.


They lose their value when the function terminates. It can be
accessed only in that function. All variables when declared

10
Data Structures with ‘c’
within the function are, by default, ‘automatic’. However, we
can explicitly declare them by using the keyword ‘automatic’.

e.g.

void print()
{
auto int i =0;

printf(“\n Value of i before incrementing is %d”,


i);
i = i + 10;
printf(“\n Value of i after incrementing is %d”, i);

main()
{
print();
print();
print();
}

Output:

Value of i before incrementing is : 0


Value of i after incrementing is : 10
Value of i before incrementing is : 0
Value of i after incrementing is : 10
Value of i before incrementing is : 0
Value of i after incrementing is : 10

1.15.2. Static Variables

Static variables have the same scope s automatic


variables, but, unlike automatic variables, static variables
retain their values over number of function calls. The life of
a static variable starts, when the first time the function in
which it is declared, is executed and it remains in existence,
till the program terminates. They are declared with the
keyword static.

e.g.

void print()
{
static int i =0;

11
Data Structures with ‘c’
printf(“\n Value of i before incrementing is %d”,
i);
i = i + 10;
printf(“\n Value of i after incrementing is %d”, i);

main()
{
print();
print();
print();
}

Output:

Value of i before incrementing is : 0


Value of i after incrementing is : 10
Value of i before incrementing is : 10
Value of i after incrementing is : 20
Value of i before incrementing is : 20
Value of i after incrementing is : 30

It can be seen from the above example that the value of


the variable is retained when the function is called again. It
is allocated memory and is initialized only for the first
time.

1.15.3. External Variables

Different functions of the same program can be written in


different source files and can be compiled together. The scope
of a global variable is not limited to any one function, but
is extended to all the functions that are defined after it is
declared. However, the scope of a global variable is limited
to only those functions, which are in the same file scope. If
we want to use a variable defined in another file, we can use
extern to declare them.

e.g.

// FILE 1 – g is global and can be used only in main()


and // // fn1();

int g = 0;

void main()
{

12
Data Structures with ‘c’
:
:
}

void fn1()
{
:
:
}

// FILE 2 If the variable declared in file 1 is required


to be used in file 2 then it is to be declared as an
extern.

extern int g = 0;

void fn2()
{
:
:
}

void fn3()
{
:
}

1.15.4. Register Variable

Computers have internal registers, which are used to


store data temporarily, before any operation can be performed.
Intermediate results of the calculations are also stored in
registers. Operations can be performed on the data stored in
registers more quickly than on the data stored in memory. This
is because the registers are a part of the processor itself.
If a particular variable is used often – for instance, the
control variable in a loop, can be assigned a register, rather
than a variable. This is done using the keyword register.
However, a register is assigned by the compiler only if it is
free, otherwise it is taken as automatic. Also, global
variables cannot be register variables.

e.g.

void loopfn()

13
Data Structures with ‘c’
{
register int i;

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


{
printf(“%d”, i);
}
}

1.16.Dynamic allocation and de-allocation of memory

Memory for system defined variables and arrays are


allocated at compilation time. The size of these variables
cannot be varied during run time. These are called ‘static
data structures’.

The disadvantage of these data structures is that they


require fixed amount of storage. Once the storage is fixed if
the program uses small memory out of it remaining locations
are wasted. If we try to use more memory than declared
overflow occurs.

If there is an unpredictable storage requirement,


sequential allocation is not recommended. The process of
allocating memory at run time is called ‘dynamic allocation’.
Here, the required amount of memory can be obtained from free
memory called ‘Heap’, available for the user. This free memory
is stored as a list called ‘Availability List’. Getting a
block of memory and returning it to the availability list, can
be done by using functions like:
-
- malloc()
- calloc()
- free()

1.16.1. Function malloc(size)

This function is defined in the header file <stdlib.h>


and <alloc.h>. This function allocates a block of ‘size’
bytes from the heap or availability list. On success it
returns a pointer of type ‘void’ to the allocated memory. We
must typecast it to the type we require like int, float etc.
If required space does not exist it returns NULL.

Syntax:

ptr = (data_type*) malloc(size);

14
Data Structures with ‘c’
where
- ptr is a pointer variable of type data_type.
- data_type can be any of the basic data type, user
defined or derived data type.
- size is the number of bytes required.

e.g.

ptr =(int*)malloc(sizeof(int)*n);

allocates memory depending on the value of variable n.

# include<stdio.h>
# include<string.h>
# include<alloc.h>
# include<process.h>

main()
{
char *str;

if((str=(char*)malloc(10))==NULL) /* allocate memory for


string */
{
printf(“\n OUT OF MEMORY”);
exit(1); /* terminate the program
*/

}
strcpy(str,”Hello”); /* copy hello into str
*/
printf(“\n str= %s “,str); /* display str */
free(str); /* free memory */
}

In the above program if memory is allocated to the str, a


string hello is copied into it. Then str is displayed. When it
is no longer needed, the memory occupied by it is released
back to the memory heap.

1.16.2. Function calloc(n,size)

This function is defined in the header file <stdlib.h>


and <alloc.h>. This function allocates memory from the heap or
availability list. If required space does not exist for the
new block or n, or size is zero it returns NULL.

15
Data Structures with ‘c’
Syntax:

ptr = (data_type*) malloc(n,size);

where
-
ptr is a pointer variable of type data_type.
-
data_type can be any of the basic data type, user
defined or derived data type.
- size is the number of bytes required.
- n is the number of blocks to be allocated of size
bytes.
and a pointer to the first byte of the allocated region is
returned.

e.g.

# include<stdio.h>
# include<string.h>
# include<alloc.h>
# include<process.h>

main()
{
char *str = NULL;

str=(char*)calloc(10,sizeof(char)); /* allocate memory


for
string */
if(str == NULL);
{
printf(“\n OUT OF MEMORY”);
exit(1); /* terminate the program
*/

}
strcpy(str,”Hello”); /* copy hello into str
*/
printf(“\n str= %s “,str); /* display str */
free(str); /* free memory */
}
1.16.3. Function free(block)

This function frees allocated block of memory using


malloc() or calloc(). The programmer can use this function and
de-allocate the memory that is not required any more by the
variable. It does not return any value.

1.17.Dangling pointer problem.


16
Data Structures with ‘c’
We can allocate memory to the same variable more than
once. The compiler will not raise any error. But it could lead
to bugs in the program. We can understand this problem with
the following example.

# include<stdio.h>
# include<alloc.h>

main()
{
int *a;

a= (int*)malloc(sizeof(int));
*a = 10; ----> 10

a= (int*)malloc(sizeof(int)); ----> 20
*a = 20;

In this program segment memory allocation for variable


‘a’
Is done twice. In this case the variable contains the address
of the most recently allocated memory, thereby making the
earlier allocated memory inaccessible. So, memory location
where the value 10 is stored, is inaccessible to any of the
application and is not possible to free it so that it can be
reused.

To see another problem, consider the next program


segment:

main()
{
int *a;

a= (int*)malloc(sizeof(int));
*a = 10; ----> 10

free(a);
----> ?
}

Here, if we de-allocate the memory for the variable ‘a’


using free(a), the memory location pointed by ‘a’ is returned
to the memory pool. Now since pointer ‘a’ does not contain any

17
Data Structures with ‘c’
valid address we call it as ‘Dangling Pointer’. If we want to
reuse this pointer we can allocate memory for it again.

1.18. Structures

A structure is a derived data type. It is a combination


of logically related data items. Unlike arrays, which are a
collection of similar data types, structures can contain
members of different data type. The data items in the
structures generally belong to the same entity, like
information of an employee, players etc.

The general format of structure declaration is:

struct tag
{
type member1;
type member2;
type member3;
:
:
}variables;

We can omit the variable declaration in the structure


declaration and define it separately as follows :

struct tag variable;

e.g.

struct Account
{
int accnum;
char acctype;
char name[25];
float balance;
};

We can declare structure variables as :

struct Account oldcust;

We can refer to the member variables of the structures by


using a dot operator (.).

e.g.

18
Data Structures with ‘c’
newcust.balance = 100.0

printf(“%s”, oldcust.name);

We can initialize the members as follows :

e.g.

Account customer = {100, ‘w’, ‘David’, 6500.00};

We cannot copy one structure variable into another. If


this has to be done then we have to do member-wise assignment.

We can also have nested structures as shown in the


following example:

struct Date
{
int dd, mm, yy;
};

struct Account
{
int accnum;
char acctype;
char name[25];
float balance;
struct Date d1;
};

Now if we have to access the members of date then we have


to use the following method.

Account c1;

c1.d1.dd=21;

We can pass and return structures into functions. The


whole structure will get copied into formal variable.

We can also have array of structures. If we declare array


to account structure it will look like,

Account a[10];

Every thing is same as that of a single element except


that it requires subscript in order to know which structure we
are referring to.

19
Data Structures with ‘c’
We can also declare pointers to structures and to access
member variables we have to use the pointer operator ->
instead of a dot operator.

Account *aptr;

printf(“%s”,aptr->name);

A structure can contain pointer to itself as one of the


variables, also called self-referential structures.

e.g.

struct info
{
int i, j, k;

info *next;
};

In short we can list the uses of the structure as:

- Related data items of dissimilar data types can be


logically grouped under a common name.
- Can be used to pass parameters so as to minimize
the number of function arguments.
- When more than one data has to be returned from the
function these are useful.
- Makes the program more readable.

1.19.Enumerated Constants

Enumerated constants enable the creation of new types and


then define variables of these types so that their values are
restricted to a set of possible values. There syntax is:

enum identifier {c1,c2,...}[var_list];

where
- enum is the keyword.
- identifier is the user defined enumerated data
type, which can be used to declare the variables in
the program.
- {c1,c2,...} are the names of constants and are
called enumeration constants.
- var_list is an optional list of variables.

e.g.

20
Data Structures with ‘c’
enum Colour{RED, BLUE, GREEN, WHITE, BLACK};

Colour is the name of an enumerated data type. It makes


RED a symbolic constant with the value 0, BLUE a symbolic
constant with the value 1 and so on.

Every enumerated constant has an integer value. If the


program doesn’t specify otherwise, the first constant will
have the value 0, the remaining constants will count up by 1
as compared to their predecessors.

Any of the enumerated constant can be initialised to have


a particular value, however, those that are not initialised
will count upwards from the value of previous variables.

e.g.

enum Colour{RED = 100, BLUE, GREEN = 500, WHITE, BLACK =


1000};

The values assigned will be RED = 100,BLUE = 101,GREEEN =


500,WHITE = 501,BLACK = 1000

You can define variables of type Colour, but they can


hold only one of the enumerated values. In our case RED, BLUE,
GREEEN, WHITE, BLACK .

You can declare objects of enum types.

e.g.

enum Days{SUN, MON, TUE, WED, THU, FRI, SAT};


Days day;
Day = SUN;
Day = 3; // error int and day are of different types
Day = hello; // hello is not a member of Days.

Even though enum symbolic constants are internally


considered to be of type unsigned int we cannot use them for
iterations.

e.g.

enum Days{SUN, MON, TUE, WED, THU, FRI, SAT};


for(enum i = SUN; i<SAT; i++) //not allowed.

There is no support for moving backward or forward from


one enumerator to another.

21
Data Structures with ‘c’
However whenever necessary, an enumeration is
automatically promoted to arithmetic type.

e.g.

if( MON > 0)


{
printf(“ Monday is greater”);
}

int num = 2*MON;

1.20.Unions

A union is also like a structure, except that only one


variable in the union is stored in the allocated memory at a
time. It is a collection of mutually exclusive variables,
which means all of its member variables share the same
physical storage and only one variable is defined at a time.
The size of the union is equal to the largest member
variables. A union is defined as follows:

union tag
{
type memvar1;
type memvar2;
type memvar3;
:
:
};

A union variable of this data type can be declared as follows,

union tag variable_name;

e.g.

union utag
{
int num;
char ch;
};

union tag ff;

22
Data Structures with ‘c’
The above union will have two bytes of storage allocated
to it. The variable num can be accessed as ff.sum and ch is
accessed as ff.ch. At any time, only one of these two
variables can be referred to. Any change made to one variable
affects another.

Thus unions use memory efficiently by using the same


memory to store all the variables, which may be of different
types, which exist at mutually exclusive times and are to be
used in the program only once.

In this chapter we have studies some advanced


features of C. We have seen how the flexibility of language
allowed us to define a user-defined variable, is a combination
of different types of variables, which belong to some entity.
We also studies arrays and pointers in detail. These are very
useful in the study of various data structures.

23
Data Structures with ‘c’

You might also like