Introduction to c



Download 0.91 Mb.
Page7/13
Date28.05.2018
Size0.91 Mb.
#51782
1   2   3   4   5   6   7   8   9   10   ...   13

}

print(char *name)



{

printf(“%s \n”, name);

while(*name)

{

printf(“%c \n”, *name);

name++; }

getch();



}
More than One Dimension

Columns







0

1

2

3




0

p+0

10

20

30

40




Rows 1

p+1

50

60

70

80




2

p+2

90

91

92

93










*(p+2)+0

*(p+1)+2

*(*(p+1)+2)

p  pointer to first row

p + i  pointer to ith row

*(p + i)  pointer to first element in the ith row

*(p + i)+j  pointer to jth element in the ith row

*(*p+ i)+j)  value stored in the jth element of ith row [i.e., cell(i, j) ]

A two-dimensional array is nothing but a collection of a number of one-dimensional arrays placed one after another. In memory whether it is a one-dimensional or it is a two-dimensional the elements are stored in one continuous chain.



row 1

row 2

row 3

10

20

30

40

50

60

70

80

90

91

92

93

6000

6002

6004

6006

6008

6010

6012

6014

6016

6018

6020

6022

‘C’ treats parts of arrays as arrays. i.e., each row of a two dimensional array can be treated as a one dimensional array.

Thus, int arr[3][4];

Can be thought of as setting up a one-dimensional array of three elements, each of which is a one-dimensional array, 4 elements long. By default we can refer to an element of a one-dimensional array using a single subscript. Similarly, if we imagine arr to be a one-dimensional array, then we can refer to its zeroth element as arr[0], the next element as arr[1] and so on.

If we execute the statement,



printf(“%u”, arr[0] );

We expect the 0th element to get printed and in the case of a two-dimensional array the 0th element is a one-dimensional array. By default by mentioning a one-dimensional array gives its base address. Therefore the printf() print the base address of the 0th one-dimensional array. Similarly arr[1] would give the address of 1st one-dimensional array.



e.g.:- main()

{

int arr[3][4]={

{10,20,30,40},

{50,60,70,80},

{90,91,92,93}

};

int i, j;

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

printf(“\n Address of %d th one-dimensional array = %u “, i, arr[i] );



}

Will print,

Address of 0th one-dimensional array = 6000

Address of 1th one-dimensional array = 6008

Address of 2th one-dimensional array = 6016

Once the two-dimensional array is declared, there onwards arr is treated as pointer to zeroth element of the two-dimensional array. Hence the expression (arr+0) gives the address of the zeroth element of the two-dimensional array. Therefore the expression *(arr+0) give the value of zeroth element. But the zeroth element of two-dimensional array is a one-dimensional array and on mentioning one-dimensional array we get its base address. Hence *(arr+0) gives the address of the zeroth one-dimensional array. i.e., arr[0] = *(arr+0) = 6000. Similarly arr[1] = *(arr+1) = base address of the 1st one-dimensional array, 6008. Therefore the expression arr[i] or *(arr+i) would give the base address of ith one-dimensional array.

Now, we have to access each individual elements of a row. For example we want to refer to the element arr[2][1] using pointers. From the above example we got the address of 2nd one-dimensional array (arr[2]) as 6016. Therefore (6016+1) will give the address 6018 and the value at this address can be obtained by using the expression *(arr[2]+1). But using the concept one-dimensional array arr[2] is the same as *(arr+2). Therefore *(arr[2]+1) is the same as *(*(arr+2)+1). i.e., arr[2][1], *(arr[2]+1) and *(*(arr+2)+1) all are refer to the same element.

Type definition

‘C’ allows user-defined data-types with the help of typedef keyword.



syntax: typedef type new_name;

Where type refers to an existing data type.

Using this method users are not actually creating a new data type, but just defining a new name for an existing data type.

e.g.:- typedef float marks;

This statement tells the compiler to recognise marks as another name for float. This can be later used to declare variables.



e.g.:- marks arr[10];

Here arr[10] is a floating point array of type marks, which is another name for float. The main advantage of typedef is that we can create meaningful data type names for increasing the readability of the program.



Type Conversion

Automatic Type Conversion: ‘C’ allows mixing of constants and variables of different types in an expression. But during the evaluation it keeps the rules of type conversion. i.e., if the operands are of different types, the lower type is automatically converted to the higher type before the operation proceeds. The result is of the higher type. The order is as shown below, the higher is first and the lower is last.

long double


double
float
unsigned long int
long int
unsigned int
int

All char are automatically converted to int. By default ‘C’ converts all floating-point operands to double precision. The final result of an expression is converted to the type of the variable on the left of the assignment sign before assigning the value to it.



Type Casing: Casing is a temporary conversion of a data object from one type to another. Casts utilise parenthesised data type to convert the representation of the object that the cast immediately precedes. Only basic data types of char, int, float, double, enum and pointers are used with casting.

syntax: (type) expression

Where type is one of the fundamental data types in ‘C’. The expression can be constant, variable or an expression (calculation).

e.g.: -


printf(“%d”, (int) 7.42);
printf(“%f”, (float)10/8 );
printf(“%d”, (int) (10.3+4.8) );
printf(“%f”, pow((double)2, (double)3) );

Q) Write a program to calculate the result of the following expression,
1/1+1/2+1/3+…+1/10.

main()


{

float sum=0;

int n;

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



sum = sum+1/(float)n;

printf(“Result = %f”, sum);



}

Will print, Result = 2.9290.

Casts are useful when working with pointers. The concept that a pointer variable contains the address of another object, whose value may be retrieved using indirection (*) is declared on the fact that a pointer variable is not just an address but it also points to a particular type of data representation. Just as normal variables may be cast to alter the form of representation, pointer variables may be cast to alter the interpretation of the variable type being pointed to.

Dynamic Memory Allocation

‘C’ language requires the number of elements in an array to be specified at the compile time. When the program compiled the space for that specified no. of elements of the array is reserved in the memory. Once specific memory locations have been reserved for a variable/array, these locations are fixed, whether they are used or not. The memory allocated at the compile time is released only when the program/function terminates and if the initially given array size is wrong, there is a chance of failure of the program or wastage of memory space.

‘C’ allows specifying an array’s size at run time. i.e., at the time of execution, or when the program needed. The process of allocating memory at run time is known as dynamic memory allocation. Under dynamic allocation scheme storage space is allocated to a program and released back to the system (OS) while the program is running.

In ‘C’ dynamic memory allocation is accomplished by using four library functions known as memory management functions.

Arrays can be defined as pointer variables since an array name is actually a pointer to the first element of the array.

e.g.:- int *d; char *c; etc

But no fixed block of memory is allocated to pointer variables used as arrays. In order to use a pointer variable as an array, it is necessary to allocate memory to it before the array elements are processed. This can be done by memory management functions.



Memory management Functions

malloc()

syntax: void *malloc(size);

malloc() reserves a continuous block of memory of specified size (in bytes) and returns a pointer to the newly allocated block’s first byte (base address) or NULL if not enough space exists for the new block. Also if the size is zero, it returns NULL.

malloc() returns a pointer of type void, therefore you can assign it to any type of pointer after type casting.

i.e., ptr = (type *) malloc(size);

e.g.:-

int *d;
d = (int *) malloc(10 * sizeof(int) );



The above example allocates a memory of 10 times the size of int type and returns the base address of the allocated memory block to the pointer d.

Sample Program: Write a program to store marks of 10 students

# include

# include

main()


{

int *p, i;

p = (int *) malloc( 10 * sizeof(int) );

if(p == NULL)



{

printf(“Not enough Memory”);

return;

}

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



{

printf(“ Enter mark %d : ”, i+1);

scanf(“%d”, p);

p++;


}

p­ – =10;

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

{

printf(“ %d : ”, *p);

p++;

}

}

calloc()

syntax: void *calloc(n, bolck_size);

calloc() is used to allocate memory space at run time for storing derived data types such as arrays and structures. While malloc() allocates a single block of memory, calloc() allocates multiple blocks of memory. calloc() also initialises all the elements by a value of zero.

ptr = (type *) calloc( n, size);

Allocates continuous space for n blocks of given size. All bytes are initialised to zero and a pointer to the base address of the allocated block is returned. If there is not enough space, a NULL pointer is returned.

e.g.:-

int *x;
x = (int *) calloc( 3, 4*sizeof(int) );



Sample Program: To accept numbers to a 3x4 matrix

main()
{


int *x, i, j;
x = (int ) calloc( 3, 4*sizeof(int) );
for( i=0; i<12; i++)
{
printf(“ Enter Number %d : ”, i+1);
scanf(“%d”, x); x++;
}
x– =12;
for(i=0; i<3; i++)
{
for(j=0; j<4; j++)
{
printf(“%d \t”, *(*(x+i*3)+j) );
}
printf(“\n”);
}
}

Q) Write a program to accept 5 names and print those names. (use calloc() )

# include


# include
main()
{
char *names;
int x;
names = (char *) calloc(5, 21*sizeof(char));
if(names == NULL)
{
printf(“Not enough Memory”); exit(-1);
}
for(x=0; x<5; x++)
{
printf(“enter name %d : ”, x+1);
scanf(“%20s”, (names+x*21));
}
names – = 5*21;
for(x=0; x<5; x++)
printf(“%d \t %s \n”, x+1, (names+x*21) );
getch();
}

free()

syntax: free(pointer_to_memory_block);

Compile time storage of a variable is allocated and released from the memory by the system in accordance with its storage class. But the memory allocated using the dynamic memory allocation functions, at run time will not be released from the memory even if it is not required. We can release the block of memory allocated by malloc() or calloc() functions using the free().



e.g.:- free(ptr);

Where ptr is a pointer to a memory block which has been created by malloc() or calloc().



Realloc()

syntax: realloc(ptr, new_size);

realloc() is used to increase or decrease the previously allocated block of memory to new_size ( in bytes) and returns the address of the reallocated block which can be different than the original address. The contents of the old block will be moved to the new block if the address of the reallocated block changes from the original. i.e., the old data will be the same even after the reallocation of memory. If the function is unsuccessful in locating additional space, it returns a NULL pointer and the original block is freed (lost).



Sample Program:

# include


# include
main()
{
char *place;
if((place = (char *) malloc(10)) == NULL)
{
printf(“Not enough Memory”);
exit(-1);
}
strcpy(place, “Trichur”);
if((place = (char *) realloc(place, 20)) == NULL)
{
printf(“Not enough Memory, data lost”);
exit(-1);
}
printf(“Contents of place = %s \n”, place);
strcpy(place, “Thiruvananthapuram”);
printf(“Contents of place = %s \n”, place);
free(place);
}

Structures in C

A structure is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling. Structures are called records in many languages like COBOL, FoxPro etc. Structures help to organise complicated data, particularly in large programs, because they permit a group of related variables to be treated as a unit instead of as separate variables. for example, a student record’s details like Rollno, name, house, pin, sex etc. or employee details like employee no., name, salary, sex etc.

Structure, which is a data structure whose individual data element can differ in type. A data structure may contain variables of basic data types, arrays, pointers and other data structures except the structure itself. The individual structure elements are called members of the structure.

Defining a Structure

A structure must be defined in terms of its individual members. Structure declaration starts with the keyword struct followed by a name for the structure called structure tag. The various fields ie., members are declared within a block of code enclosed by braces. the member declaration is as the same as that of an ordinary variable declaration. After the closing brace of the block a semicolon must terminate the structure declaration.



syntax:

struct tag



{

type member-1;

type member-2;

........................

type member-n;

};

The member names within a structure must be unique through member names can be the same as the variable name defined outside of the structure. The rules of forming a tag name and individual member names are the same as for other variables. the member can not be initialised within a structure declaration. Declaration tells only a model (template) of type struct tag and no space is allocated in the memory. Space is allocated only when a structure variable is declared.



e.g.:-

struct emp



{

int emp_no;

char name[20];

float salary;

char sex;

};

Structure Variable Declaration

syntax: struct tag variable1, variable2;

Where variable1, variable2 are variables of type struct tag.



e.g.:- struct emp salesman, officer;

Where salesman and officer are variables of type struct emp.

It is also possible to combine structure declaration and variable declaration in one statement.

syntax: struct tag

{

type member-1;

type member-2;

........................

type member-n;

} variable1, variable2;

The tag is optional in this case.



e.g.:-

struct emp



{

int emp_no;

char name[20];

float salary;

char sex;

} salesman, officer;

Referencing Fields (members) of Structure

Referencing a field means accessing its contents. A structure field is referred to by writing the name of the structure variable followed by a dot(.), followed by the name of the field(member).



syntax: structure_vriable_name.member_name

Where dot(.) is called the structure member operator, which connects the structure_variable name and the member_name.



e.g.:-

salesman.salary = 2345.23;

officer.sex = ‘M’;

strcpy(officer.name, “Ajith”);

salesman.name[0] = ‘A’;

officer.emp_no++;

officer.name[0] = toupper(officer.name[0]);

ptr = &officer.salary;



Initialisation of Structure Variables

The initialisation is done by giving an equal sign after the variable name, followed by a list of values for the members (fields) enclosed in a pair of braces. The closing brace is followed by a semicolon. Initialisation can be done only at the time of structure variable declaration and not at the time of execution.



e.g.:- struct emp officer = { 420, “Ajith”, 1200.50, ‘M’ };

Copying of structure Variables

Although structure is a derived data type, structure variable is a valid operand for the assignment operator. A structure variable can be copied into another variable of the same structure type, by the assignment operator.

For example, if the contents of officer1 were to be copied into officer2,
officer2 = officer1;
To copy the content of one structure variable into another you can also copy individual members of the entire structure.

For example if the contents of officer1 were to be copied into officer2,


officer2.mp_no = officer1.emp_n;
strcpy(oficer2.name, officer.name);
officer2.salary = officer1.salary;
officer2.sex = officer1.sex;

Another method would be to use the memcpy()



syntax: memcpy( &dest, &source, n_bytes);

This function copies n_bytes starting from the address of source to a block of n_bytes starting from the address of destination. The size of the structure, i.e., n_bytes can be obtained by using the sizeof() operator.



e.g.:- memcpy(&officer2, &officer1, sizeof(struct emp) );

Arrays of Structure

To declare an array of structures first define a tructure and then declare an array variable of that typr. For example, to declare a 10 element array of structures of type struct emp that had been defined earlier,



struct emp employee[10];

Like all aray variables, array of structures begins subscripting at 0. The arry name followed by its subscript enclosed in square brackets stands for an element of that array.

To print the name of the second element in the array,

printf(“name of second employee : %s”, employee[1].name );

To copy the fifth element of the array to he seventh element,



memcpy(&employee[6], &employee[4], sizeof(struct emp) );

Initialisation of structure arrays

e.g.:- struct emp employee[3] = { { 101, “Ajith”, 7812, ‘M’ },

{ 102, “Sunil”, 4352, ‘M’ },

{ 103, “Santhi”, 5412, ‘F’ }

};

Each element of the array is initialised as if some structure variable is being initialised. The whole list is then enclosed within braces to indicate that the array is being initialised.



Structures within a Structure

In ‘C’ the components of a structure can be anything but the structure itself. Therefore we can have one structure within another to group the data more meaningfully.



e.g.:-

struct sal



{

float basic;

float hra;

float da;



};

struct emp

{

int emp_no;

char name[20];

struct sal salary;

char sex;

};

struct emp emp1, emp2;

To access the name of emp1,

emp1.name

To access the basic of emp1,

emp1.salary.basic

To initialize, struct emp emp2 = { 1001, “Ajith”, {1000.45, 150, 435.55 }, ‘M’ };

Passing Structures as Arguments to Functions (Call by Value)

A structure variable can be passed as an argument to another function. This is a useful facility because it is used to pass groups of logically related data items together instead of passing them one by one. ‘C’ also supports to pass the members of a structure individually.



Q) Write a program to accept the details of 10 employees. The program should print a report of all employees’ details and the net salary.

# include

struct sal

{

float basic;

float hra;

float da;



};

struct emp



{

int emp_no;

char name[20];

float salary;

char sex;

};

float total(struct sal salary);

main()

{

struct emp employee[10];

int i;

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



{

printf(“Enter Employee Number : “); scanf(“%d”, &employee[i].emp_no);

printf(“Enter Employee Name : “); scanf(“%s”, employee[i].name);

printf(“Enter Basic Salary : “); scanf(“%f”, &employee[i].salary.emp_no);

printf(“Enter HRA : “); scanf(“%f”, &employee[i].salary.hra);

printf(“Enter DA : “); scanf(“%f”, &employee[i].salary.da);

printf(“Enter Sex : “); scanf(“%c”, &employee[i].sex);

}

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



{

float x;


x = total(employee[i].salary);

printf(“%d \t %s \t %f \t %f \t %f \t %f \n”, employee[i].emp_no, employee[i].name, \

employee[i].salary.basic, employee[i].salary.hra, employee[i].salary.da, x);

}

}

float total(struct sal salary)



{

return(salary.basic + salary.hra + salary.da);



}

Pointers to Structures

‘C’ allows pointers to structures just as it allows pointers to any other type of variable. Like other pointers we can declare structure pointers by placing an indirection operator in front of pointer variable name.



e.g.:- struct emp *emp_ptr;

There are two major uses for structure pointers, to generate a call by reference call to a function and to create linked lists and other dynamic data structures using ‘C’s dynamic memory allocation system.

To find the address of a structure variable, place the & operator before the variable name.

e.g.:- struct emp emp1, emp_ptr;

emp_ptr = &emp1;

In order to access the elements (members) of a structure using a pointer to that structure use the operator.



e.g.:- emp_ptrsex = ‘F’;

is the same as



e.g.:- emp1.sex = ‘F’;

The is called the arrow operator, and consists of the minus sign followed by the greater than sign.

The declaration of a pointer allocates space for the pointer and not what it points to. Therefore it is necessary to make the pointer to point to a place in memory containing some legal address of that type.

e.g.:- emp_ptr = &emp1;

Sometimes it is necessary that storage for a variable be allocated at the time of execution as and when needed. This is done using C’s dynamic memory allocation function malloc().



e.g.:- emp_ptr = (struct emp *) malloc(sizeof(struct emp));

The programmer can use the dot (.) operator to access the member of structure pointer instead of using the arrow () operator, using the format, (*ptr).member. For example to access the member salary, you can use (*emp_ptr).salary instead of emp_ptrsalary. The parentheses around *emp_ptr are necessary because the member operator (.) has a higher precedence than the indirection operator (*).
Q) Write a program to accept the details of 10 employees. The program should print a report of all employees’ details and the net salary. [Use pointer to structure]

e.g.:-

# include

struct sal

{

float basic;

float hra;

float da;



};

struct emp



{

int emp_no;

char name[20];

struct sal salary;

char sex;

};

float total(struct sal salary);

main()

{

struct emp employee[10], *emp_ptr, *temp;

int i;

emp_ptr = employee; temp = emp_ptr;



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

{

printf(“Enter Employee Number : “); scanf(“%d”, &emp_ptremp_no);

printf(“Enter Employee Name : “); scanf(“%s”, emp_ptrname);

printf(“Enter Basic Salary : “); scanf(“%f”, &emp_ptrsalary.emp_no);

printf(“Enter HRA : “); scanf(“%f”, &emp_ptrsalary.hra);

printf(“Enter DA : “); scanf(“%f”, &emp_ptrsalary.da);

printf(“Enter Sex : “); scanf(“%c”, &emp_ptr.sex);

emp_ptr++;



}

emp_ptr = temp;

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

{

float x;


x = total(emp_ptrsalary);

printf(“%d \t %s \t %f \t %f \t %f \t %f \n”, emp_ptremp_no, emp_ptr.name, \

emp_ptrsalary.basic, emp_ptrsalary.hra, emp_ptrsalary.da, x);

emp_ptr++;



}

}

float total(struct sal salary)



{

return(salary.basic + salary.hra + salary.da);



}

Passing Structure Pointers as Arguments to Functions (Call by reference)

Structure pointers can be passed to functions by declaring the formal parameters of the function as a structure pointer and at the time of calling the function, a pointer to the structure or the address of a structure variable is passed to the function. Using this method i.e., Call by Reference the elements of the structure can be modified in the function.



Type definition of Structures: The typedef feature is useful when defining structures, since it eliminates the need to repeatedly write struct tag whenever a structure is referenced.

syntax:

typedef struct



{

type member-1;

type member-2;

........................

type member-n;

} new_type;

Where new_type is the user-defined structure type and not a structure variable. Structure variables can now be defined in terms of the new data type.



e.g.:-

typedef struct



{

int emp_no;

char name[20];

float salary;

char sex;

} employee;

employee x, y;

Where employee is the new data type and x and y are variables of type employee.

Q) Write a program to accept the details of 10 employees. The program should print a report of all employees’ details and the net salary. [Use dynamic memory allocation, call by reference and pointer to structure]

e.g.:-

# include

typedef struct

{

float basic;

float hra;

float da;



} sal;

struct emp



{

int emp_no;

char name[20];

sal salary;

char sex;

};

typedef struct emp empl;

float total(sal *salary);

main()


{

empl *emp_ptr, *ptr;

int i;

emp_ptr = (empl *) malloc(10 * sizeof(empl) );



ptr = emp_ptr;

if(ptr == NULL)



{

printf(“ Not enough Memory “);

return;

}

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



{

printf(“Enter Employee Number : “); scanf(“%d”, &emp_ptremp_no);

printf(“Enter Employee Name : “); scanf(“%s”, emp_ptrname);

printf(“Enter Basic Salary : “); scanf(“%f”, &emp_ptrsalary.emp_no);

printf(“Enter HRA : “); scanf(“%f”, &emp_ptrsalary.hra);

printf(“Enter DA : “); scanf(“%f”, &emp_ptrsalary.da);

fflush(stdin);

printf(“Enter Sex : “); scanf(“%c”, &emp_ptr.sex);

emp_ptr++;

}

emp_ptr = ptr;

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

{

float x;


x = total(&emp_ptrsalary);

printf(“%d \t %s \t %f \t %f \t %f \t %f \n”, emp_ptremp_no, emp_ptr.name, \

emp_ptrsalary.basic, emp_ptrsalary.hra, emp_ptrsalary.da, x);

emp_ptr++;



}

}

float total(sal *salary)



{

return(salarybasic + salaryhra + salaryda);



}

Self-Referential Structures

‘C’ allows to include within a structure one member that is a pointer to the parent structure type.



syntax:

struct tag



{

type member-1;

type member-2;

........................

type member-n;

struct tag *ptr;



};

Where ptr refers to the name of a pointer variable. Thus the structure of type tag will contain a member that points to another structure of type tag. Such structures are known as self-referential structures.

Self-referential structures are very useful to create linked data structures like linked lists. Linked lists are used to create arrays of unknown size in memory. i.e., the size of linked list can grow or shrink during the execution of a program, because a linked list is a dynamic data structure. But array’s size is fixed at the time of compilation. Linked lists allow items to be inserted and deleted quickly and easily.

Linked lists can be circularly, singly or doubly linked. A singly linked list contains a link to the next data item. Each data item called node consists of a structure that includes information fields and a link pointer and the last item’s link pointer points to the NULL, to terminate the linked list.


FILE HANDLING IN C

The 'C' file system works with a wide variety of devices including keyboard, monitor, printers, disk drives etc. Even though all these devices are very different from each other. The buffered file system transforms each device into a logical device called stream. All streams act similarly and hence there is no difficulty in handling these different types of devices. The functions used are all common for any device.



Streams and Files

The ‘C’ I/O system provides an interface to the 'C' programmer independent of the actual device being accessed. This interface is not actually a file but an abstract representation of the device. This abstract interface is known as a stream and the actual device is called the file. There are two types of streams, the text and binary streams.



TEXT STREAMS

A text stream is a sequence of characters. The text streams can be organised into lines terminated by a Newline character. Newline characters are optional in the last line as this line can be terminated using the end-of-file mark. In a text stream, certain characters may undergo translations as required by the host environment (operating system) for example, a Newline (\n) can be converted to a carriage return (‘\r’) and linefeed pair. Therefore, there may not be a one to one relationship between the characters read or written and those on the external device. Also because of translations, the number of characters read or written may not be same as that on the actual device. Under DOS, the character Ctrl-Z (ASCII 26) is used in text streams indicates the end-of-file.



BINARY STREAMS

A binary stream is a sequence of bytes with a one-to-one correspondence to those in the external device. There are no character translations as in the text streams. The number of bytes read or written is the same as the number on the actual device. Binary streams are a sequence of bytes, which do not have any flags to indicate the end of file or end of record. End-of-file is determined by the size of the file.



FILE POINTERS

A file pointer is a pointer to information that defines various details about the file, like filename, status, current position of file pointer, etc. i.e., the file pointer identifies a specific disk file and is used by the associated stream to direct the operation of the buffered I/O functions. A file pointer is a pointer variable to the data type FILE. FILE is a derived data type and its structure is defined in stdio.h.

In order to read or write files the program needs to use file pointers.

E.g. :- FILE *fp, *fp1, fp2;

OPENING STREAMS USING fopen()

The fopen() opens a stream for use and links a file with that stream and returns the file pointer associated with that file. If an error occurs when it is trying to open a file then fopen() returns a NULL pointer. The prototype of fopen() is as follows,



FILE fopen(char *filename, char *mode);

where filename is a pointer to string of characters that make up a valid filename and may include a path specification and the mode determines how the file will be opened. The mode can be one of the following,



r

Open a text file for reading

w

Create a text file for writing

a

Append to a text file

rb

Open a binary file for reading

wb

Create a binary file for writing

ab

Append to a binary file

r+

Open a text file for read/write

w+

Create a text file for read/write

a+

Open/`Create a text file for read/write

rb+

Open a binary file for read/write

wb+

Create a text file for read/write

ab+

Open a binary file in append mode for read/write

Using w mode removes the file if already present. Therefore it is better to check for existence of the file before opening in the write mode. If the mode is a the file is opened with the current contents safe. If the specified file does not exist, a file created with the specified filename.

e.g.:-


main()

{

FILE *fp;

………

fp=fopen("test","w");



if(fp==NULL)

{

printf('Cannot open the file");

exit(1);

}

………


}

If a file is opened for read/write operations, it will not be erased if it exists. If it does not exist, it will be created.



CLOSING STREAMS

Closing a stream frees various system resources and flushes out the associated buffers that prevents loss of data when writing to a disk.



fclose()

prototype : int fclose(FILE *stream);

Where stream is the file pointer. The function returns an integer value, 0, if successful else returns EOF.

fcloseall()

prototype : int fcloseall(void);

This function closes all open streams and returns the number of streams closed or EOF if any error is detected.

e.g.:-


main()

{

int fcl;


…….

fcl=fcloseall();

if(fcl= =EOF)

printf("Error closing files");

else

printf("%d file(s) closed",fcl);



………

}

READING AND WRITING STREAMS

fgetc()

prototype : int fgetc(FILE *stream);

The fgetc() returns the next character from the input stream from the current position and increments the file position indicator. The character is read as an unsigned char that is converted to an integer. if the end-of-file is reached, fgetc() returns EOF.

e.g.:-


# include

main()


{

char filename[13];

char ch;

FILE *fp;

printf("Enter Filename : ");

gets(filename);

if((fp=fopen(filename,"r"))==NULL)

{

printf("Cannot open %s",filename);

exit(1);

}

while((ch=fgetc(fp))!=EOF)



{

putchar(ch);



}

fclose(fp);



}

Since EOF is a valid integer value, you must use feof() to check for end-of-file when working with binary file.



feof()

prototype : int feof(FILE *stream);

The feof() checks the file position indicator to determine if the end of file associated with stream has been reached. A non-zero (true) value is returned if the file position indicator is at the end of file. Otherwise, the function returns zero (false).

eg:-


while(!feof(fp))

{

putchar(fgetc(fp));



}

fputc()

prototype : int fputc(int ch, FILE *stream);

The fputc() writes a character to a specified stream at the current file position and then advances the file position indicator . If successful, fputc() returns the value of the character written, otherwise EOF is returned.

e.g.:-


# include

main()


{

char filename[13],ch;

FILE *fp;

printf("Enter filename : ");

gets(filename);

if((fp=fopen(filename, "w"))==NULL)



{

printf("Cannot open %s",filename);

exit(1);

}

while((ch=getch())!='X' )



{

fputc(ch, fp);



}

fclose(fp);



}

getc()

prototype : int getc(FILE *stream);

same as fgetc();

putc()

prototype : int putc(int ch, FILE *stream);

same as fputc();

fgets()

prototype : char *fgets(char *string, int length, FILE *stream);

fgets() reads a string from the specified stream until either a newline(\n) character is read or length-1 characters have been read. If a newline is read, it would be a part of the string, unlike gets(). The resultant string would be NULL terminated. The function returns a pointer to string if successful and a NULL pointer if an error occurs.

fputs()

prototype : int fputs(char *string, FILE *stream);

fputs() writes the string to the specified file stream starting at the current location, unless the stream is opened in append mode in which case it is written at EOF.

Sample program to simulate DOS COPY command

# include

main()


{

FILE *in, *out;

char str[81], file1[13], file2[13];

printf("Enter source file name : ");

gets(file1);

printf("Enter target file name : ");

gets(file2);

if((in=fopen(file1, "r"))==NULL)



{

printf("%s not found"); exit(1);



}

if((out=fopen(file2, "w"))==NULL)



{

printf("Cannot create %s ", file2);

exit(1);

}

while(!feof(in))



{

if(fgets(str, 81, in))



{

fputs(str, out);



}

}

fcloseall();



}

FLUSHING STREAMS

The fclose() and fcloseall() flush the streams before closing the files. But if you want to flush the contents of streams without closing them, use the fflush() and flushall() functions.

The action of flushing depends upon the file type. A file opened for read will have its input buffer cleared, while a file opened for write will have its output buffer written to the file.

fflush()

prototype : int fflush(FILE *stream);

Flush the buffer leaving the file stream open. returns 0 on success and EOF on failure.

flushall()

prototype : int flushall(void);

flush all buffers leaving files open. Returns the number of files flushed.

STANDARD STREAMS

Whenever a 'C' program starts execution under DOS five special file streams are opened automatically by the operating system. They are the standard input (stdin), the standard output(stdout), standard error(stderr), standard printer(stdprn) and the standard auxiliary(stdaux). The stdin is assigned to the keyboard, stdout and stderr are assigned to the monitor, stdprn is assigned to the first parallel printer port and stdaux is assigned to the first serial port. They are defined as fixed pointers of type FILE.



Sample program to print the contents of a file to printer

# include

main()


{

FILE *in;

char buffer[81],filename[13];

printf("Enter file name to print : ");

gets(filename);

if((in=fopen(filename, "r"))==NULL)



{

fputs("File not found", stderr);

exit(1);

}

while(!feof(in))



{

if(fgets(buffer, 81, in))



{

fputs(buffer, stdprn);



}

} fclose(in);

}

CURRENT ACTIVE POINTER

In order to keep track of the position where I/O operations are to take place, a pointer is maintained in the FILE structure. Whenever a character is read from or written to the stream the current active pointer is advanced. Most of the I/O functions refer to current active pointer and update it after the input or output procedures on the stream.



ftell()

prototype : long int ftell(FILE *fp);

The ftell() returns a long int value which gives the position of current active pointer in the specified stream.

e.g.:- printf("Current position = %ld", ftell(fp));



SETTING CURRENT POSITION

Immediately after opening the stream the current active pointer position is set to zero and points to the first byte of the stream. Whenever a character is read from or written to the stream, the current active pointer is automatically advanced, the pointer may be set to any position other than the current pointer, at any time in the program.



rewind()

prototype : void rewind(FILE *fp);

Rewind() takes the current active pointer back to the beginning of the stream from any point on the stream. This function allows to restart the file without closing the file and reopening it.

fseek()

prototype : int fseek(FILE *fp, long int offset, int origin);

fseek() repositions the current active pointer by a specified number of bytes from either the start, the current position or the end of the stream, depending upon the position specified to the fseek(). The offset is the number of bytes beyond the file location given by origin. Origin must be one of the values 0,1 or 2 which represent three symbolic constants defined in stdio.h as follows

SEEK_SET or 0 - Beginning of file

SEEK_CUR or 1 - Current file pointer position

SEEK_END or 2 - End-of-file

A return value of zero means fseek() succeeded and non-zero value means failure.

e.g.:- fseek(fp, 0L, SEEK_SET);

The above example takes the pointer to the beginning of the stream which is equivalent to rewind(fp);

The offset may be positive, meaning move forwards or negative meaning move backwards.

e.g.:-

fseek(fp, 0L, 0); - go to beginning(rewind)



fseek(fp, 0L, 1); - stay at the current position

fseek(fp, 0L, 2); - go to the end-of-file, past the last character of the file

fseek(fp, 10L, 0); - move to 11th byte in the file

fseek(fp, 10L, 1); - move forward by 10 bytes

fseek(fp, -m, 1); - go backward by m bytes from the current position

fseek(fp, -m, 2); - go backward by m bytes from the end

When the operation is successful, fseek() returns a zero. If the program attempts to move the file pointer beyond the file boundaries an error occurs and fseek returns -1(non-zero).

fprintf() and fscanf()

The functions fprintf and fscanf perform I/O operations that identical to the familiar printf and scanf functions, except that they work on files.

syntax : fprintf(FILE *stream, "control string", arguments);

fprintf sends output to the specified file stream. fprintf returns the number of bytes output. In case of error, it returns EOF.

syntax : fscantf(FILE *stream, "control string", arguments);

fscanf performs a formatted input from the specified stream. Returns the number of input fields successfully scanned, converted and stored, the returned value does not include unstored scanned fields.

e.g.:- fscanf(fp, "%s %d", name, &age);

fprintf(stdout," %s -- %d", name, age);



BLOCK I/O

The block I/O functions are used to read or write continuous blocks of bytes from and to a file. Each block will generally represent a complex data structure, such as a structure or an array. For example, it is easier to read or write an entire block of data rather than reading or writing the individual components (i.e., structure members) within a block separately.

prototypes : size_t fread(void *buffer, size_t num_bytes, size_t count, FILE *stream);

size_t fwrite(void *buffer, size_t num_bytes, size_t count, FILE *stream);

The data type size_t is an ANSI 'C' addition to improve portability. It is predefined as an integer type large enough to hold sizeof() results. Usually it is equivalent to unsigned integer. For fread() buffer is a pointer to the region of memory that will receive the data item from the file. For fwrite(), buffer is a pointer to the information that will be written to the file.

The number of bytes to read or written is specified by num-bytes. The argument count determines how many items(each num-bytes in length) are read or written.

The fread() returns the number of items read. This value be less than count if the end-of-file is reached or an error occurs. The fwrite() returns the number of items written. This value will equal to count unless an error occurs.

As long as the file has been opened for binary operations, fread() and fwrite() can read and write any type of information.

e.g.:-

fread(&buffer, sizeof(buffer), 1, fp);



fwrite(&buffer, sizeof(buffer), 1, fp);

ARRAY OF POINTERS

The way there can be an array of ints or an array of floats, similarly there can be an array of pointers. Since a pointer variable always contains an address, an array of pointers would be nothing but a collection of addresses. The addresses present in the array of pointers can be addresses of different variables or addresses of arrays and array elements.

syntax : data_type *array_name[size of array];

eg:- int *arr[4]; an array of 4 integer pointers

float *price[10]; an array of 10 float pointers

Sample Programs: To show array of pointers can contain the addresses of different data items.

i)

main()


{

int *arr[4];

int a=10, b=20, c=30,d=40,x=0;

arr[0]=&a; arr[1]=&b; arr[2]=&c; arr[3]=&d;

for(x=0;x<4;x++)

{

printf("%u -- %d\n", arr[x], *(arr[x]) );



}

getch();


}

will print, The for loop in the program picks up the addresses present in

6784 - - 10 the elements of arr and prints the addresses present at there 6786 - - 20 and the values present at these addresses.

6788 - - 30 ( the addresses shown in the sample output are imaginary )

6790 - - 40



ii)

main() will print, 6202 - - 5101 - - 0, where



{ 6202 is the base address of the pointer

int arr[]={0,1,2,3,4}, *p[5]; array, p, 5101 is the base address

p[0]=a; p[1]=a+1; p[2]=a+2; p[3]=a+3; p[4]=a+4; of integer array, a stored in the first

printf("%u -- %u --%d",p,*p,*(*p) ); element of p, and 0 is the value stored



} in the first element of array a.

iii)

main() will print,



{ 6202 - - 5101 - - 0

int arr[]={0,1,2,3,4}, *p[5]; 6204 - - 5103 - - 1

p[0]=a; p[1]=a+1; p[2]=a+2; p[3]=a+3; p[4]=a+4; 6206 - - 5105 - - 2

for(i=0;i<5;i++) 6207 - - 5107 - - 3

printf("%u -- %u --%d",&p[i],p[i],*(p[i]) ); 6210 - - 5109 - - 4

}

iv)

main() will print,



{ 6202 - - 5101 - - 0

int arr[]={0,1,2,3,4}, *p[5]; 6204 - - 5103 - - 1

p[0]=a; p[1]=a+1; p[2]=a+2; p[3]=a+3; p[4]=a+4; 6206 - - 5105 - - 2

for(i=0;i<5;i++) 6207 - - 5107 - - 3

printf("%u -- %u --%d", p+i,*(p+i),*(*(p+i)) ); 6210 - - 5109 - - 4

}

v)

main()


{

int arr[]={0,1,2,3,4}, *p[5],**ptr; will print,

p[0]=a; p[1]=a+1; p[2]=a+2; p[3]=a+3; p[4]=a+4; 5101 - - 0

ptr=p; 6202 - - 5101 - - 0

printf("%u - - %d \n", a, *a); 6202 - - 5101 - - 0

printf("%u - - %u - - %d \n", p, *p, **p);

printf("%u - - %u - - %d \n", ptr, *ptr, **ptr);

}

vi)

main()


{

int a[3][3]={ 1,2,3,4,5,6,7,8,9 }, *ptr[3]; will print,

ptr[0]=a[0]; ptr[1]=a[1]; ptr[2]=a[2]; 1 - - 4 - - 7

for(i=0;i<3;i++) 1 - - 4 - - 7

printf("%d \t", *ptr[i] );

printf("\n");

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

printf("%d \t", *a[i] );



}

Array of pointers mainly used for storing several strings in memory.

e.g.:- char *name[]={ "joy", "srinivas", "jayaprakash", "antony", "hussain", "abijith"};



In the above example names[] is an array of 6 pointers. It contains addresses of given names. i.e., base address of "joy" is stored in names[0], base address of "antony" is stored in names[3] and so on.

If we used a two-dimensional array to store these names, where each row will be of equal lengths, it will take roughly 72 bytes in memory.

But by using the memory of pointers is strings will take only 41 bytes. Thus one reason to store to make more efficient use of available memory.

Another advantage is that easy manipulation of strings. e.g.:- exchange of positions of strings like in sorting of string arrays.



e.g.1:- string exchanging:

main()


{

char *names[]={"joy", "srinivas", "jayaprakash", "antony", "hussain", "abijith"};

char *temp;

printf("Before exchanging : ");

printf("%s - - %s", names[2], names[3] );

temp = names[2];

names[2] = names[3];

names[3] = temp;

printf("\n After exchanging : ");

printf("%s - - %s", names[2], names[3] );

getch();

}

will print,

Before exchanging : jayaprakash - - antony

After exchanging : antony - - jayaprakash

e.g. 2: sorting strings

main()


{

char *names[]={"joy", "srinivas", "jayaprakash", "antony", "hussain", "abijith"};

sort(names);

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

printf("%s \n", arr[i] );

getch();


}

sort( char *names[])



{

int i, j;

char *temp;

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



{

for(j=5; j>i; j--)



{

if(strcmp(names[j], names[j-1]) < 0)



{

temp=names[j];

names[j]=names[j-1];

names[j-1]=temp;



}

}

}

}

e.g.3 :- sorting strings

main()


{

char *names[]={"joy", "srinivas", "jayaprakash", "antony", "hussain", "abijith"};

sort(names);

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

printf("%s \n", arr[i] );

getch();


}

sort( char *names[])



{

int i,j;


char *temp;

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



{

for(j=5; j>i; j--)



{

if(strcmp(names[j], names[j-1]) < 0)



{

swap(&names[j], &names[j-1] );



}

}

}

}

swap(char *s1, char *s2)



{

char *temp;

temp = *s1;

*s1= *s2;

*s2= temp;

}

LIMITATION OF ARRAY OF POINTERS TO STRING

When we are using a two dimension array of characters we can either initialise the strings where we are declaring the array or receive(accept) the string using scanf(). However, when we are using an array of pointers to strings we can initialise the strings at the place where we are declaring the array, we cannot accept the strings from keyboard using scanf().

i.e., code like,

…………..


…………..

char *names[3];

printf("Enter a name : ");

scanf("%s", name[i] );

…………

…………


Doesn't work because when we are declaring the array it is containing garbage values, and it is wrong to send these garbage values to scanf() as the addresses where it should keep the strings received from the keyboard.

Write a function month_name(int n);, which returns a pointer to a character string containing the name of the nth month.

char *month_name(int n)



{

static char *name[]={ "Illegal Month Number", "January", "February", "March", "April", "May",\


"June", "July", "August", "September", "October", "November", "December"

};

return( (n<1 || n>12) ? name[0] : name[n]) ;



}

COMMAND LINE ARGUMENTS

Command line arguments are the information made available to the program by writing it after the program name on the command line of the operating system. The command line arguments are used to pass information into the main() when program begins executing.



main() can take two arguments usually called argc and argv and the information contained in the command line is passed on to the main() through these arguments, when main() is called up by the system.

The variable argc is an argument counter, which holds the number of arguments including the program name on the command line, and is an integer. The argv is an argument vector and represents an array of character pointers that point to the command line arguments. Any type of argument in the command line is treated as a string. The size of this array will be equal to the value of argc. You can access the individual arguments by indexing (subscripting) argv.

The first parameter in the command line is always the program name and therfore argv[0] always represents the program name, argv[1] points to the first argument and so on. The argcth element points to NULL.

syntax : main( int argc, char *argv[])



{

.......


......

}

for example, if we want to execute a program to copy the contents of a file named file1 to another file named file2, then the command line like,



C:\>DUP file1 file2

Where DUP is the executable program file name. Here argc is 3 and argv is as shown below,



Q). Write a program to simulate the ECHO command available under DOS

# include

main(int argc, char *argv[])

{

int i;


for(i=1; i
{

printf("%s\t",argv[i] );



}

}

OR

# include

main(int argc, char *argv[])

{

int i=1;


while(argv[i]!=NULL)

{

printf("%s\t",argv[i++] );



}

}

OR

# include

main(int argc, char *argv[])

{

int i=1;


while(i
{

printf("%s\t",*argv );

argv++;

}

}

if any numeric value contained in the command line arguments will also passed to the main() as a string. Therefore you must convert it to numeric before processing using library functions like atoi(), atol(), atof() etc.



Q. Write a program to print the specified no. of dots(.) .

/* dots.c */

# include

void main(int argc, char *argv[])



{

int i;


if(argc < 2)

{

printf("You must specify a number");

printf(" with the program name \n");

printf("Try again");

exit(1);

}

for(i=1; i<=atoi(argv[1]); i++)

putchar('.');
printf("%s completed", argv[0]);

putchar(7); /* to create a beep sound */



}

An example program execution, C:\> dots 20



A program with command line arguments should issue instructions, if the user attempts to run the program without proper information. The command line arguments give the program a professional appearance. It is not necessary that you should always use the variable names argc and argv. In place of them any other valid variable name can be used.

Q1. Write a program tail, which prints the last 'n' lines of the specified file. By default, n is 10. But it can be changed by an optional command line argument. ie., tail file1 should print the last 10 lines of file1, and tail file2 15 should print the last 15 lines of file2.

Q2. Write a program head, which prints the first n lines of the specified.(use command line arguments)

Q3. Write a program to simulate the DOS external command FILD which prints the lines of the given file that contains the given word. There are options that changes the default meaning.

n - line numbers before each line

c - only total no. of lines

i - ignore case difference

x - will print each line that doesn’t match the given word

FUNCTIONS RETURNING POINTERS

The way functions return an int, a float, a double or any other data type, it can also return a ointer(address). To make a function return a pointer it has to be mentioned in the calling function as well as in the function declaration (prototype).



Syntax: datatype *function-name (arguments);

eg: (1)

# include

main()

{

int *p;


int *function();

p=function;

printf("%u -- %d",p,*p);

}

s

int *function()



{

int i=20;

return(&i);

}

eg: (2)

# include

float *function(float *r);

main()


{

float p=45.67, q=28.94, *x;

c=&p;

printf("Before calling *x = %f\n",*x);



x=function(&p);

printf("After calling *x = %f\n",*x);

getch();

}

float *function(float *r)



{

r=r+1;


return(r);

}

eg: (3)

# include

char *addstring(char *s1, char *s2);

main()


{

char s1[30],s2[10], *ptr;

printf("Enter first string : "); gets(s1);

printf("Enter second string : "); gets(s2);

ptr=addstring(s1,s2);

printf("\nAfter addition S1 = %s",ptr);

getch();

}

char *addstring(char *s1, char *s2)



{

int i,j;


for(i=0; s1[i]!=NULL; i++); Version 1

for(j=0; s2[j]!=NULL; s1[i]=s2[j],i++,j++);

return s1;

}

char *addstring(char *s1, char *s2)



{

int i,j;


for(i=0; s1[i]!=NULL; i++); Version 2

for(j=0; (s1[i]=s2[j])!=NULL;i++,j++);

return s1;

}

char *addstring(char *s1, char *s2)



{

char *ptr=s1;

for(;*s1!=NULL;s1++);s1--; Version 3

for(;(*s1=*s2)!=NULL;s1++,s2++);

return ptr;

}

char *addstring(char *s1, char *s2)



{

char *ptr=s1;

for(;*s1++!=NULL;);s1--; Version 4

for(;(*s1++=*s2++)!=NULL;);

return ptr;

}

char *addstring(char *s1, char *s2)



{

char *ptr=s1;

for(;*s1++;); s1--; Version 5

for(;*s1++ = *s2++;);

return ptr;

}

POINTERS TO FUNCTIONS

Even though a function is not a variable it has a physical location in memory that can be assigned to a pointer. A function’s address is the entry point of the function. Because of this, a function pointer can be used to call a function.

In ‘C’, as each function is compiled, source code is transformed into object code and an entry point is established. When a call is made to a function while your program is running, a machine language call is made to this point. Therefore, if a pointer contains the address of a function’s entry point, it can be used to call that function.

To obtain the address of a function, use the function’s name without any parentheses and arguments.



syntax: datatype (*ptr)();

The above declaration tells that ptr is a pointer to a function which returns the specified datatype.

eg: int (*ptr)();

ptr = strcmp;

To invoke the function using function pointer use the syntax, (*ptr)();

eg: to invoke the strcmp function,

(*ptr)(string1,string2);

Sample Program (1) To show functions also have address

# include

main()

{

int show();

printf(“Address of function show = %u “,show);

show();


}

show()


{

printf(“This is a sample program”);



}

Sample Program (2) Using pointer to function

# include

int show();

main()


{

int (*ptr)();

ptr=show;

printf(“Address of function show = %u “,ptr);

(*ptr)(); /* to invoke the function show using the pointer to function */

}

show()


{

printf(“This is the second sample program”);



}

Sample Program (3) Comparing two strings for equality

# include


# include

void check( char *a, char *b, int (*cmp)() );

main()

{

char s1[80],s2[80];



int (*ptr)();

ptr=strcmp;

printf(“Enter first string : “); gets(s1);

printf(“Enter second string : “); gets(s2);

check(s1,s2,ptr);



}

void check(char *a, char *b, int (*cmp)() )



{

if( !(*cmp)(a,b) )

printf(“Equal”);

else


printf(“Not equal”);

}

UNIONS

A union is a memory location that is shared by two or more different variables, generally of different types, at different times. Defining a union is similar to defining a structure, using the keyword union.



syntax :


Download 0.91 Mb.

Share with your friends:
1   2   3   4   5   6   7   8   9   10   ...   13




The database is protected by copyright ©ininet.org 2024
send message

    Main page