Saturday, January 23, 2010

11.2.5 THE BITWISE ASSIGNMENT OPERATORS

C also contains the following bitwise assignment operators.
             &=       ^=       |=       <<=         >>=

These operators combine the preceding bitwise operations with ordinary assignments.The left operand must be an assignable integer-type identifier (e.g., an integer variable), and the right operand must be a bitwise expression. The left operand is interpreted as the first operand in the bitwise expression. The value of the bitwise expression is then assigned to the left operand. For example, the expression
a &= 0x7f is equivalent to a = a & 0x7f.

The following program displays the bit pattern corresponding to a signed decimal integer.

#include < stdio.h >


main ()
{
int a, b, m, count, nbits;
unsigned mask;


/*determine the word size in bits and set the initial mask*/
nbits = 8* sizeof (int);
m = 0x1 << (nbits - 1);     /*place 1 in leftmost position*/


/*main loop*/
do {
/*read a signed integer*/
printf("\n\nEnter an integer value (0 to stop): ", a);
scanf("%d", &a);


/*output the bit pattern*/
mask = m;
for (count = 0; count <= nbits; count++)  {
   b = (a & mask) ? 1 : 0;    /*set the display bit on or off*/
   printf("%x", b);               /*print display bit*/

   if (count % 4 == 0)
       printf(" ");      /*blank space after every 4th digit*/
   mask >>= 1;  /*shift mask 1 position to the right*/

}

}


The program is written so that it is independent of the integer word size. Therefore it can be used on any computer.            

Wednesday, January 20, 2010

11.2.4 THE SHIFT OPERATORS

The two bitwise shift operators are shift left (<<) and shift right (>>). Each operator requires two operands.The first is an integer type operand that represents a bit pattern to be shifted. The second is an unsigned integer that indicates the number of displacements (i.e., whether the bits in the first operand will be shifted by 1 bit position, 2 bit positions, 3 bit positions, etc.). This value cannot exceed the number of bits associated with the word size of the operand.

Suppose a is an unsigned integer variable whose value is 0x6db7. The expression
b = a << 6;

will shift all bits of a six places to the left and assign the resulting bit pattern to the unsigned integer variable b. The resulting value of b will be 0x6dc0.


 
All of the bits originally assigned to a are shifted to the left 6 places..The rigtmost 6 bits positions are filled with 00 0000.

11.2.3 MASKING

Masking is a process in which a given bit pattern is transformed into another bit pattern by means of a logical bitwise operation.The original bit pattern is one of the operands in the bitwise operation.The second operand called the mask, is a specially selected bit pattern that brings about the desired transformation.

There are several kinds of masking operations.For example, a portion of a given bit pattern can be copied to a new word, while the remainder of the new word is filled with 0s.Thus, part of the original bit pattern will be "masked off" from the final result. The bitwise and operator is used (&) is used for this type of masking operation as illustrated below.

Suppose a is an unsigned integer variable whose value is 0x6db7.Extract the rightmost 6 bits of this value and assign them to the unsigned integer variable b. Assign 0s to the 10 leftmost bits of b.

To carry out this operation, we write the bitwise expression
b= a & 0x3f;

The second operand 0x3f will serve as the mask. Thus the resulting value of b will be 0x37.

The validity of this result can be established by examining the corresponding bit patterns.

             a = 0110 1101 1011 0111
       mask = 0000 0000 0011 1111
                  ---------------------------------
             b = 0000 0000 0011 0111
               = 0x37
The mask prevents the leftmost 10 bits from being copied from a to b.

Suppose once again a is an unsigned integer variable whose value is 0x6db7.Now extract the leftmost 6 bits of this value and assign them to the unsigned integer variable b.Assign 0s to the 10 rightmost bits of b.

To carry out this operation, we write the bitwise expression
b= a & 0xfc00;

Another type of masking allows a portion of a given bit pattern to be copied to a new word, while the remainder of the new word is filled with 1s.The bitwise or (|) operator is used for this purpose.

A portion of a given bit pattern can be copied to a new word, while the remainder of the original bit pattern is inverted within the new word.The bitwise exclusive or (^) operator is used for this type of masking.

Thursday, January 14, 2010

11.2.2 THE LOGICAL BITWISE OPERATORS

There are three logical bitwise operators: bitwise and (&), bitwise or (|), bitwise exclusive or (^). Each of these operators requires two integer-type operands.The operations are carried out independently on each pair of corresponding bits within the operands.Thus, the least significant bits (i.e. the rightmost bits) within the two operands will be compared. The results of these comparisons are:
1. A bitwise and expression will return a 1 if both bits, have a value of 1 (i.e., if both bits are true). Otherwise,  it will return a value of 0.
2.  A bitwise exclusive or expression will return a 1 if one of the bits has a value of 1 (i.e., if one bit is true, the other is false). Otherwise,  it will return a value of 0.
3. A bitwise or expression will return a 1 if one or more bits, have a value of 1 (i.e., if one or both bits are true). Otherwise,  it will return a value of 0.

The results are summarized in the following table.


b1

b2

b1&b2

b1^b2

b1|b2

1

1

1

0

1

1

0

0

1

1

0

1

0

1

1

0

0

0

0

0

Wednesday, January 13, 2010

11.2.1 THE ONE'S COMPLEMENT OPERATOR

The one's complement operator (~) is a unary operator that causes the bits of its operand to be inverted (i.e., reversed) so that 1s become 0s and 0s become 1s. This operator always precedes its operands.The operand must be an integer-type quantity (including integer, long, short, unsigned, char, etc.).

Consider the simple C program shown below.


#include


main()
{
unsigned i = 0x5b3c;


printf("hexadecimal values: i = %x  ~i = %x\n", i, ~i);
printf("decimal equivalents: i = %u  ~i = %u\n", i, ~i);
}

Executing this program on a computer with a 16-bit word size results in the following output.


hexadecimal values: i = 5b3c    ~i = a4c3
decimal equivalents: i = 23356  ~i = 42179

To understand these results, first consider the bit patterns corresponding to the values for i and ~i.

i = 0101 1011 0011 1100
~i = 1010 0100 1100 0011

Therefore the decimal equivalents are

i= 23356 and ~i= 42179

11.2 BITWISE OPERATIONS

Some applications require the manipulation of individual bits within a word of memory. Assembly language or machine language is normally required for operations of this type. However, C contains several special operators that allow such bitwise operations to be carried out easily and efficiently.These bitwise operators can be divided into three general categories: the one's complement operator, the logical bitwise operators and the shift operators. C also contains several operators that combine bitwise operations with ordinary assignment.Each category is discussed separately below.

11.1 REGISTER VARIABLES

In chapter 6 we mentioned that there are four different storage class specifications in C, and we discussed about three of them- automatic, external and static- in detail. We now turn our attention to the last of these, which is the register storage class.

Registers are special storage areas within the computer's central processing unit. The actual arithmetic and logical operations that comprise a program are carried out within these registers.Normally, these operations are carried out by transferring information from the computer's memory to these registers, carrying out the indicated operations, and then transferring the results back to the computer's memory. This general procedure is repeated many times during the course of a program's execution.

The register and automatic storage classes are closely related.In particular, their visibility (i.e. their scope) is the same. Thus, register variables, like automatic variables, are local to the function in which they are declared. Furthermore, the rules governing the use of register variables are the same as those for automatic variables, except that the address operator(&) cannot be applied to register variables.

The program presented below is a variation of that shown in Section 6.4, for generating a series of Fibonacci numbers.

/* calculate the first 23 Fibonacci numbers 10,000,000 times, to illustrate the use of register variables*/




#include < stdio.h >
#include < time.h >
 
long int fibonacci  (int count);


main()
{
time_t start, finish;  /* start and finish times*/
int count, n=23;
long int loop, loopmax = 1000000;
register int f , f1, f2;

/* tag the starting time*/
time(&start);

/* do multiple loops*/
for (loop=1; loop <= loopmax; ++loop)  {
f1=1;
f2=1;
 
/*generate the first n Fibonacci numbers*/
for (count=1;count <= n;++count)   {
f= (count < 3)? 1 : f1+f2;
f2=f1;
f1=f;
}
}

/* adjust the counter and tag the completion time*/
--count;
time(&finish);

/*display the output*/
printf ("i= %d  F= %d\n", count, f);
printf ("elapsed time: %0.1f seconds", difftime (finish, start));
}


The program includes three integer variables that have the register storage class. Only 23 Fibonacci numbers will be calculated, since the Fibonacci numbers are represented as ordinary integer variables (higher Fibonacci numbers will generate an integer overflow).The calculation of the Fibonacci numbers is repeated 10,000,000 times, in order to obtain a reasonably accurate assessment of the time required to execute the program.

The variables start and finish are of type time_t, as defined in the header file time.h.The program also makes use of the library function difftime, which returns the time difference defined  by the variables finish and start.



When executed on a Pentium-class desktop computer, the following results were obtained:

i=23  F=28657
elapsed time: 37 seconds

If the program is rerun without the register declaration (i.e., if the variables f, f1 and f2 are declared as ordinary integer variables), the output is essentially the same.When run with an older desktop computer, however the use of register class resulted in a 36 percent reduction in computer time.

Tuesday, January 12, 2010

11. LOW-LEVEL PROGRAMMING

From the materials presented in the first 10 chapters, it should be clear that C is a full fledged, high-level programming language. However C also possesses certain "low-level" features that allow the programmer to carry out operations normally available only in assembly language or or machine language.For example, it is possible to store the values of certain variables within the central processing unit's registers.This will usually speed up any computation associated with these values.

C also permits the manipulation of individual bits within a word.Thus, bits can be shifted to the left or the right, inverted (1s and 0s reversed), or masked (extracted selectively).Furthermore, C allows the bits within a word of memory to be organized into individual groups.This permits multiple data items to be packed within a single word.This chapter shows how to carry low-level operations in C.

10.5 CONCEPT OF BINARY FILES

So far we have discussed mostly about data files (commonly known as text files).Every computer uses another kind of files also that are known as binary files. All machine language files are actually binary files.For opening a binary file, file has to be mentioned as "rb" or "wb" in fopen command. Otherwise all files are opened in default mode, which is the text mode. So mentioning mode as "r" or "w" in fopen is always equivalent to "rt" or "wt" respectively. Here t stands for text (files) and b stand for binary (files).It may be noted that text files can also be stored and processed as binary files but not vice-versa.The binary files differ from text files in two ways mainly:
1.The storage of newline characters
2. The eof character

First difference is about the storage of \n, i.e. newline character. In text files, \n is stored as a single newline character by user, but it takes 2 bytes of storage inside the memory. Are you surprised? Actually newline characters is collection of two characters- first carriage return(\r, ASCII code 13), and second line feed(ASCII code 10). When a text file \n, it is stored using 2 bytes, but is considered as a single character. So when you try to count the number of characters of a text file, each newline character contributes by one.

Text files and binary files also differ in the way of handling end of file. Although this may not make much difference to the users, but it may become a reason of failure of certain programs.Hence, this difference is crucial to understand.The eof corresponds to the character having ASCII code 26 for text files only. In binary files there is no such explicit eof character. The binary files do not store any special character at the end of file  and their file-end is verified by using their size itself.Now if a text file is opened in binary mode for reading purposes, it may detect eof wrongly as a result of a particular data having ASCII code 26.

While discussing binary files, one more point is worth mentioning  and corresponds to storage of data in binary format. The numbers can be written to files using fprintf statement (putc and fputc can't be used for this purpose).The fprintf statements stores numbers as sequence of alphabets. So storage of 1001 in files (text as well as binary) using fprintf will be done as sequence of 4 alphabets '1', '0', '0', '1'. It means that storage of a 4-digit number will take 4 bytes. But on a 16-bit computer, an integer needs 2 bytes of storage and this concept can be utilized with help of fwrite statement (in both text as well as binary mode). The fwrite will store every integer value by taking two bytes not equal to the number of digits in that integer value.

Let us write a C program which stores numbers in binary format using fwrite.

/* store 1001 to 1100 in a binary file using fwrite */


#include < stdio.h >
#include < conio.h >


main()
{
FILE *fp;
int i;
if ((fp = fopen("binval.dat", "wb")) == NULL)
  printf("\n ERROR- Cannot create the designated file\n");
else  {
    for  (i=1001; i <= 1100; i++)
      fwrite (&i, sizeof (int), 1, fp);

}
fclose(fp);
getch();

}

The size of the output file created with the help of the above program will be 200 bytes (2 bytes for each of 1000 integers). If the same data was to be stored in a text file using fprintf statement, the size of the file would be minimum 4000 bytes (4 x 1000).

Monday, January 11, 2010

10.4 UNFORMATTED DATA FILES

Some applications involve the use of data files to store blocks of data, where each block consists of a fixed number of contiguous bytes. Each block will generally represent a complex data structure, such as a structure or an array.For example, a data file may consist of multiple structures having the same composition, or it may contain multiple arrays of the same type and size. For such applications it may be desirable to read the entire block from the data file, or write the entire block to the data file, rather than reading or writing the individual components (i.e. structure members or array elements) within each block separately.

The library function fread and fwrite are used in this kind of situations. These functions are often referred to as unformatted read and write functions.Similarly, data files of this type are often referred to as unformatted data files.

Each of these functions require four arguments: a pointer to the data block, the size of the data block, the number of data blocks being transferred, and the stream pointer. Thus a typical fwrite function can be written as

   fwrite (&customer, sizeof (record), 1, fpt);

where customer is a structure variable of type record, and fpt is the stream pointer associated with a data file that has been opened for output.

Sunday, January 10, 2010

10.3 READING AND WRITING A DATA FILE

Files are generally  used for two purposes- reading and writing. Whenever we want to store something into a file, the file needs to be opened in write mode. If the contents to be stored in the output file are already known then the then the file can also be created directly using a text editor or word processor. In that case the file is obviously a formatted stream-oriented data file. However, if the contents to be stored are not pre-defined and are computed generated with the help of a program itself. then the output file can be created with help of a program only.

The following program reads all the numbers from the input file values.dat and stores average of these numbers in an output file named as average.res

#include < stdio.h >
#include < conio.h >


main()
{
FILE *fpin, *fpout;
float val, avg, sum=0;
int count=0;


if ((fpin = fopen("values.dat", "r"))== NULL)
   /*open the data file for reading purposes*/
  printf("\nERROR - cannot open the designated file\n");

else  {/*read and display each character from the file*/
   while(!feof(fpin))  {
fscanf(fpin, "%f", &val);
sum += val;
count++;

}
}
avg = sum/count;
if ((fpout = fopen("average.res", "w")) == NULL)
   /*open the data file for writing purposes*/
  printf("\nERROR- cannot open the designated file\n");
else  /*write the average in the file*/
  fprintf(fpout, "The average of numbers of file values.dat is %.3f\n", avg);


fclose(fpin);
fclose(fout);
getch();


}

If the average value computed is 81.563, then the output line will have the following line

The average of numbers of file values.dat is 81.563

10.2 OPENING AND CLOSING A DATA FILE

When working with a stream-oriented data file, the first step is to establish a buffer area,where information is temporarily stored while being transferred between the computer's memory and the data file.The buffer area allows information to be read from or written to the data file more rapidly than would otherwise be possible.

The buffer area is established by writing

FILE  *ptvar;

where FILE (uppercase letters required) is a special structure type that establishes the buffer area, ptvar is a pointer variable that indicates the beginning of the buffer area. The structure type FILE is defined within a system include file, typically stdio.h. The pointer ptvar is often referred to as a stream pointer, or simply a stream.

A data file must be opened before it can be created or processed.this associates the file name with the buffer area (i.e.with the stream). It also specifies how the data file will be utilized, i.e. as a read only file, a write-only file, or a read/write file,in which both operations are permitted.

The library function fopen is used to open a file.This function is typically written as

ptvar = fopen (file-name, file-type);

where file-name and file-type are strings that represent the name of  the data file and the manner in which the data file will be utilized. The name chosen for the file-name must be consistent with the rules for naming files, as determined by the computer's operating system.The file-type must be one of the strings shown in the following table.



File-Type

Meaning

"r"
Open an existing file for reading only.

"w"
Open a new file for writing only. If a file with the
specified file-name currently exists, it will be destroyed and a
new-file created in its place.

"a"
Open an existing file for appending (i.e., for adding the
information at the end of the file). A new file will be created if the
file with the specified file-name does not exist.

"r+"
Open an existing file for both reading and writing.

"w+"
Open a new file for both reading and writing. If a file with
the specified file-name currently exists, it will be destroyed and
a new file will be created in its place.

"a+"
Open an existing file for both reading and appending. A new
file will be created if the file with the specified file-name does
not exist.

Finally a file must be closed at the end of the program.This can be acomplished with the library function fclose. The syntax is simply

fclose (ptvar);

It is good programming practice to close a data file explicitly using the fclose function, though most C compilers will automatically close a data file at the end of a program execution.

Saturday, January 9, 2010

10.1 WHY FILES

There can be many reasons to use files.The first and foremost reason is need of permanency of storage of data.If we want to preserve the output of any program for future use, it is not possible without files.All messages or values printed with help of any output statements like printf, putchar, etc. are never available for future use and the only solution in that case is to write the output in files.Similarly, if there is a large amount of data generated as output by a program, storing that output in file will help in easy handling/analysis of the output, as user can see the whole output at any time even after complete execution of the program.

The above situations explain the need of output files.The input files are also equally important and useful.if a program needs a lot of data to be inputted, user cannot keep on typing again and again for repeated execution of the program.In that case, all input data can be once written in a file and then that file can be easily used as the input file.moreover the transfer of input-data and/or output-data from one computer to another can be easily done by using files.

Friday, January 8, 2010

10. DATA FILES

Many applications require that information be written to or read from an auxiliary device.Such information is stored on the memory device in the form of a data file. Thus data files allow us to store information permanently, and to access and alter information whenever necessary.

There are two different types of data files, called stream-oriented (or standard access) data files, and system-oriented (or low-level) data files.Stream oriented data files are generally easier to work with and are therefore commonly used.

Stream-oriented data files van be subdivided into  two categories - they are text files and unformatted data files.Text files consist of consecutive characters.These characters can be interpreted as individual data items, or as components of strings or numbers.Unformatted data files organizes data into blocks containing contiguous bytes of information.These blocks represent more complex data structures, such as arrays and structures.


System-oriented data files are more closely related to the computer's memory system than stream oriented data files. They are more complicated to work with, though their use may be more efficient for certain kinds of applications.

Data files are most frequently used to store user's data. However data related to numbers can be efficiently stored in binary files, which are also useful to hanle file containing machine language contents e.g. exe or .com files.The files which are stored in binary format differ significantly from data (also called as text) files.

In this chapter we are only concerned with stream oriented-data files.

9.7 UNIONS

Like structures, unions also contain members whose individual data types may differ from one another.However, the members of a union share the same storage area within the computer's memory, whereas each members within a structure is assigned its own unique storage area.thus, unions are used to conserve memory.They are useful for applications involving multiple members, where values need not be assigned to all of the members at any one time.

In general terms, the composition of a union may be defined as

union tag   {
member 1;
member 2;
...............
member n;

};


where union is a required keyword and other terms have the same meaning as in a structure definition. Individual union variables may be defined as


storage-class union tag variable 1,variable 2,............,variable n;

where storage-class is an optional storage class specifier, union is a required keyword, tag is the name that appeared in the structure declaration, and  variable 1,variable 2,............,variable n are structure variables of type tag. 

The two declarations can be combined, just as we did with structures. Thus, we can write

storage-class union tag  {
member 1;
member 2;
...............
member n;
}variable 1,variable 2,............,variable n;


The tag is optional in this situation. 


Let us consider the simple C program shown below.


#include < stdio.h >


main()
{
union id  {
char color;
int size;

 };


struct   {
char manufacture[20];
float cost;
union id description;

} shirt, blouse;


printf("%d\n", sizeof(unionid));


/*assign a value to color*/
shirt.description.color = 'w';
printf("%c %d\n", shirt.description.color, shirt.description.size);


/*assign a value to size*/
shirt.description.size = 12;
printf("%c %d\n", shirt.description.color, shirt.description.size);

}


Execution of the program results n the following output.


2
w  -24713
@ 12


The first line indicates that the union is allocated two bytes of memory, to accommodate an integer quantity.In line 2, the first data item (w) is meaningful, but the second (-24713) is not.In line 3, the first data item (@)is meaningless, but the second data (12) item has a meaning.Thus, each data item has a meaningful value in accordance with the assignment statement preceding each printf statement.

9.6 SELF-REFERNTIAL STRUCTURES

It is some times desirable to include within a structure one member that is a pointer to the parent structure type.In general terms, this can be expressed as

struct tag  {
member 1;
member 2;
..............
struct tag *name;
};

where name 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 in applications that involve linked data structures, such as lists and trees.

The basic idea of linked data structure is that each component within the structure includes a pointer indicating where the next component can be found.Therefore, the relative order of the components can easily be changed simply by altering the pointers.In addition, individual components can easily be added or deleted, again by altering the pointers. As a result, a linked data structure is not confined to some maximum number of components.Rather, the data structure can expand or contract in size as required.

Fig. 1 illustrates a  linked list containing three components.Each component consists of two data items; a string and a pointer that references the next component within the list.Thus, the first component contains the string red, the second contains green and the third contains blue.Also, the end of the list is indicated by a special pointer, called NULL.



Now let us add another component, whose value is white, between red and green. To do so, we merely change the pointers as illustrated in fig. 2. Similarly, if we choose to delete the component whose value is green, we simply change the pointer associated with the second component, as shown in fig. 3.



 


In fig. 4 we  see an example of tree. Trees consists of nodes and branches, arranged in some hierarchical manner which indicates a corresponding hierarchical structure within the data.(A binary tree is a tree in which every node has no more than two branches.)

In fig. 4 the root node has the value screen, and the associated branches lead to the nodes whose values are foreground and background, respectively. Similarly, the branches  associated with foreground lead to the nodes whose values are white, green and amber, and the branches associated with background lead to the nodes whose values are black,blue and white.







 Fig. 5 explains the manner in which pointers are used to construct tree.


Self referential structures are ideally suited for applications involving linked data structures.Each structure will represent a single component (i.e. one node) within the linked data structure.The self referential pointer will indicate the location of the next component.

Tuesday, January 5, 2010

9.5 PASSING STRUCTURES TO FUNCTIONS

There are many different ways to pass structure-type information to or from a function.Structure members can be transferred individually or the whole structure can be passed to a function.

Individual structure members can be passed to a function as arguments in the function call, a single structure member can be returned via the return statement.To do so, each structure member is treated the same as an ordinary single-valued variable.

Consider the simple C program shown below.

#include < stdio.h >

typedef struct  {
int acct_no;
char acct_type;
char *name;
float balance;
} record;

main()  /*transfer a structure-type pointer to a function*/

{
 void adjust(record *pt);   /*function declaration*/


static record customer = {"Smith", 5120, 'R', 100.35};


printf("%s  %d  %c   %.2f\n",customer.name, customer.acct_no, customer.acct_type, customer.balance); 

adjust(&customer); 

printf("%s  %d  %c   %.2f\n",customer.name, customer.acct_no, customer.acct_type, customer.balance);



}

void adjust (record *pt)     /*function definition*/
{
pt->name = "Jones";
pt->acct_no = 5635;

pt->acct_type = 'C';
pt->balance =200.65; 

return; 
}


This program illustrates the transfer of a structure to a function by passing the structure's address(a pointer) to the function.in particular, customer is a static structure of type record, whose members are assigned an initial set of values.These initial values are displayed when the program begins to execute.The structure's address is then passed to the function adjust, where different values are assigned to the members of the structure.


Execution of the program results in the following output.


Smith  5120  R  100.35
Jones  5635  C  200.65

Thus the values assigned to the members of  customer within adjust are recognized within main, as expected.

9.4 STRUCTURES AND POINTERS

The address(&) operator can be used to access the beginning address of a structure, in the same manner as any other address.Thus, if variable represents a structure type variable, then &variable represents the starting address of that variable.We can declare a pointer variable for a structure by writing

type *ptvar;

where type is a data type that identifies the composition of the structure, and ptvar represents the name of the pointer variable.The beginning address of a structure can be assigned to this pointer by writing

ptvar = &variable;

The variable declaration and pointer declaration can be combined with the structure declaration by writing

struct   {
member 1;
member 2;
...............
member n;
} variable, *ptvar;


An individual member of a structure can be accessed in terms of its corresponding pointer variable by writing

ptvar->member;


which is equivalent to writing


variable.member;


The operator -> is comparable to the period operator (.).


Consider the simple C program shown below,


#include < stdio.h >


main()
{
int n=5120;
char t = 'R';
float b = 100.35;


typedef  struct   {
int month;
int day;
int year;
} date;



struct  {
int *acct_no;
char *acct_type;
char *name;
float *balance;
date lastpayment;
} customer, *pc = &customer;


customer.acct_no = &n;
customer.acct_type = &t;
customer.name = "Smith";
customer.balance = &b;


printf("%d  %c  %s  %.2f\n", *customer.acct_no, *customer.acct_type, customer.name, *customer.balance); 

printf("%d  %c  %s  %.2f\n", *pc->acct_no, *pc->acct_type, pc->name, pc->balance); 

}


The members acct_no, acct_type, name, balance are written as pointers within the second structure.Thus, the value to which acct_no points can be accessed by writing either
*customer.acct_no or *pc->acct_no.The same way other members can also be accessed.

Execution of the program results in the following two lines of output.

5120  R  Smith  100.35
5120  R  Smith  100.35

These two lines of output are identical, as expected.

Monday, January 4, 2010

9.3 USER-DEFINED DATA TYPES (typedef)

The typedef feature allows users to define new data-types that are equivalent to existing data types.
In general terms, a new data type is defined as

typedef type new-type;

where type refers to an existing data type (either a standard data type, or previous user-defined data  type ), and new-type refers to the new user defined data type.It should be understood that the new data type will be new only in terms of name but in reality it is fundamentally same as the standard data types.

Here is a simple declaration involving the use of typedef.

typedef  int  age;

In this declaration age is a user defined data type, which is equivalent to type int.
Hence, the variable declaration

age male, female;

is equivalent to

int male, female;

The typedef is particularly convenient when defining structures, since it eliminates the need to write the struct tag  whenever a structure is referenced.

Let us now look at the following declarations

typedef  struct   {
int acct_no;
char acct_type;
char name[80];
float balance;

} record;

record oldcustomer, newcustomer;

The first declaration defines record as a user defined data type.The second declaration defines old customer and new customer as structure variables of type record.

9.2 PROCESSING A STRUCTURE

Usually the members of  structure are processed individually, as separate entities.A structure member can be accessed by writing

variable.member;

where variable refers to the name of a structure-type variable, and member refers to the name of the member within the structure.Notice the period (.) that separates the variable name from the member name.This period is an operator;it is a member of highest precedence group, and its associativity is left to right.

Let us consider the following structure declarations.

struct date  {
int month;
int day;
int year;
};

struct account  {
int acct_no;
char acct_type;
char name[80];
float balance;
struct date lastpayment;

} customer;


Here customer is a structure variable of type account.If we are to access customer's account number, we would write

customer.acct_no

Similarly, the customer's name and the customer's balance can be accessed by writing

customer.name
and
customer.balance

Sunday, January 3, 2010

9. STRUCTURE AND UNIONS

We have already discussed about array, which is a data structure whose elements are of same data type.
We will now discuss about structure, in which the individual elements  may differ in type.The individual structure elements are referred as members.

Unions resembles closely with structures,which also can have multiple members. Difference between structures and union is that unlike structures members of a union share the same storage area, even though the individual members may differ in type.

Saturday, January 2, 2010

8.10 MORE ABOUT POINTER DECLARATIONS

Several declarations involving pointers are shown below.The individual declarations range from simple to complex.

int *p;     /*p is a pointer to an integer quantity*/


int *p[10];     /*p is a 10-element array of pointers of integer quantities*/


int (*p)[10];     /*p is a pointer to a 10-element integer array */


int *p(void);     /*p is a function that returns a pointer to an integer quantity*/ 


int p(char *a);     /*p is a function that accepts an argument which is a pointer to a character and returns  an integer quantity*/


int *p(char *a);     /*p is a function that accepts an argument which is a pointer to a character and returns a pointer to an integer quantity*/


int (*p)(char *a);     /*p is a pointer to a function that accepts an argument which is a pointer to a character and returns a pointer to an integer quantity*/


int (*p(char *a))[10];  /*p  is a function that accepts an argument which is a pointer to a character  and returns a pointer to a 10-element integer quantity*/
 
int p(char (*a)[]);  /*p  is a function that accepts an argument which is a pointer to a character array and returns an integer quantity*/


int p(char *a[]);  /*p  is a function that accepts an argument which is an array of pointers to characters and returns an integer quantity*/


int *p(char a[]);  /*p is a function that accepts an argument which is an array of characters and returns a pointer to an integer quantity*/


int *p(char (*a)[]);   /*p is  function that accepts an argument which is a pointer to a character array and returns a pointer to an integer quantity*/   


int *p(char *a[]);  /*p  is a function that accepts an argument which is an array of pointers to characters and returns a pointer to an integer quantity*/


int (*p)(char (*a)[]);  /*p is a pointer to a function that accepts an argument which is is a pointer to a  character array and returns an integer quantity*/


int *(*p)(char (*a)[]);  /*p is a pointer to a function that accepts an argument which is a pointer to a character array and returns a pointer to an integer quantity*/


int *(*p)(char *a[]);  /*p is a pointer to a function that accepts an argument which is an array of pointers to characters and returns a pointer to an integer quantity*/


int (*p[10])(void);  /*p is a 10-element array of pointers to functions;each function returns an integer quantity*/


int (*p[10])(char a);  /*p is a 10-element array of pointers to functions;each function accepts an  argument which is a character, and returns an integer quantity*/


int *(*p[10])(char a);  /*p is a 10-element array of pointers to functions;each function accepts an  argument which is a character, and returns a pointer to an integer quantity*/


int *(*p[10])(char *a);  /*p is a 10-element array of pointers to functions;each function accepts an  argument which is a pointer to a character, and returns a pointer to an integer quantity*/

Friday, January 1, 2010

8.9 PASSING FUNCTIONS TO OTHER FUNCTIONS

A pointer to a function can be passed to another function as argument.This allows transferring of one function to another, as if the first function were a variable.let us refer the first function as guest function, and the second function as host function.

The formal argument declaration of the guest function can be written as

data-type (*function-name) ( )

where data-type refers to the data type of the quantity returned by the guest and function-name is the name of the guest. The formal argument declaration can also be written as


data-type (*function-name) ( type 1, type 2,..... )

or as
data-type (*function-name) ( type 1 arg 1, type 2 arg 2,..... )

where  type 1, type 2,..... refers to the data types of the arguments associated with the guest; arg 1, arg 2,....... refers to the names of  the arguments associated with the guest.

The host function declaration can now be written as

funct-data-type funct-name
                       (arg-data-type (*pt-var) ( type 1 arg 1, type 2 arg 2,..... ),
                       |<---     pointer to guest function               ----------->|
                                                     data types and names of other funct args);

where  funct-data-type refers to the data type of the quantity returned by the host function; funct-name refers to the name of host function; arg-data-type refers to the data type of the quantity returned by the guest function; pt-var refers to the pointer variable pointing to the guest function, and
type 1 arg 1, type 2 arg 2,..... refer to the data types and the corresponding names of the guest function's arguments.


The skeletal outline of a C program is shown below.This program consists of four functions: main, process, funct1 and funct2.Note that process is host function for funct1 and funct2. Each of the three subordinate functions returns an integer quantity.

int process(int (*)(int, int));       /*function declaration (host)*/
int funct1(int, int);                    /*function declaration (guest)*/

int funct2(int, int);                   /*function declaration (guest)*/

main()
{
int i, j;
...................
i = process(funct1);         /*pass funct1 to process; return a value for i*/
...................


j = process(funct2);        /*pass funct2 to process; return a value for j*/
...................


}


process(int (*pf)(int, int))    /*host function definition*/
                                        /*(formal argument is a pointer to a function */
{
int a, b, c;
...................  


c = (*pf)(a, b);             /*access the function passed to this function; return a value for c */
...................  


return(c);

}


funct1(a, b)      /*guest function definition*/

int a, b;

{
int c;

c=.........       /*use a and b to evaluate c*/


return(c);


}

funct2(x, y)      /*guest function definition*/

int x, y;

{
int z;

z=.........       /*use x and y to evaluate z*/


return(z);


}

8.8 ARRAYS OF POINTERS

A multidimensional array can be expressed in terms of an array of pointers rather than a pointer to a group of contiguous arrays, .in such situations the newly defined array will have one less dimension than the original multidimensional array. Each pointer will indicate the begining of a seperate (n-1) dimensional array.

In general terms, a two-dimensional array can be defined as a one-dimensional array of pointers by writing

data-type *array[expression 1];

rather than a conventional array definition,


data-type array[expression 1][expression 2];

Similarly, an n-dimensional array can be defined as an (n-1) dimensional array of pointers by writing

data-type *array[expression 1][expression 2]......[expression n-1];

rather than

data-type array[expression 1][expression 2]......[expression n];

In these declarations data-type refers to the data type of the original one-dimensional,array is the array name,expression 1,expression 2,......,expression n are positive-valued integer expressions that indicate the maximum number of elements associated with each subscript.

You can notice that array name and its preceding asterisk are not enclosed in parentheses in this type of declaration.

Let us now approach the problem of entering a list of strings into the computer and rearranging them into alphabetical order using a one-dimensional array of pointers, where each pointer indicates the
begining of a string.

/*sort a list of strings into alphabetical otder using an array of pointers*/

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

void reorder(int n, char *x[]);

main()
{
int i, n=0;
char *x[10];

printf("Enter each string on a seperate line below\n\n");
printf("Type \'END\' when finished\n\n");

/*read in the list of strings*/
do {
/*allocate memory*/
x[n] = (char *)malloc(12 *sizeof(char));

printf("string %d", n+1);
scanf("%s", x[n]);
}

while(strcmp(x[n++], "END"));


/*reorder the list of strings*/
reorder(--n, x);


/*display the reordered list of strings*/
printf("\n\nReordered list of strings:\n");
for (i=0; i < n;++i)
printf("\nstring  %d: %s", i+1, x[i]);
}



void reorder(int n, char *x[])    /*rearrange the list of strings*/
{

char *temp;
int i, item;


for (item=0; item < n-1; ++item)


/*find the lowest of all remaining strings*/
for (i= item+1; i < n; ++i)
if (strcmp(x[item], x[i] ) > 0)   {
/*interchange the two strings*/
temp = x[item];
x[item] = x[i];
x[i] = temp;

}

return;

}