Wednesday, November 2, 2011

Additional Features of Structures in C programming language

Let us now explore the intricacies of structures with a view of programming convenience. We would highlight these intricacies with suitable examples:
(a) The values of a structure variable can be assigned to another structure variable of the same type using the assignment operator. It is not necessary to copy the structure elements piece-meal. Obviously, programmers prefer assignment to piece-meal copying. This is shown in the following example.

main( )
{
struct employee
{
char name[10] ;
int age ;
float salary ;
} ;
struct employee e1 = { "Sanjay", 30, 5500.50 } ;
struct employee e2, e3 ;
/* piece-meal copying */
strcpy ( e2.name, e1.name ) ;
e2.age = e1.age
e2.salary = e1.salary ;
/* copying all elements at one go */
e3 = e2 ;
printf ( "\n%s %d %f", e1.name, e1.age, e1.salary ) ;
printf ( "\n%s %d %f", e2.name, e2.age, e2.salary ) ;
printf ( "\n%s %d %f", e3.name, e3.age, e3.salary ) ;
}

The output of the program would be...
Sanjay 30 5500.500000
Sanjay 30 5500.500000
Sanjay 30 5500.500000
Ability to copy the contents of all structure elements of one variable into the corresponding elements of another structure variable is rather surprising, since C does not allow assigning the contents of one array to another just by equating the two. As we saw earlier, for copying arrays we have to copy the contents of the array element by element.
This copying of all structure elements at one go has been possible only because the structure elements are stored in contiguous memory locations. Had this not been so, we would have been required to copy structure variables element by element. And who knows, had this been so, structures would not have become popular at all.
(b) One structure can be nested within another structure. Using this facility complex data types can be created. The following program shows nested structures at work.
 
main( )
{
struct address
{
char phone[15] ;
char city[25] ;
int pin ;
} ;
struct emp
{
char name[25] ;
struct address a ;
} ;
struct emp e = { "jeru", "531046", "nagpur", 10 };
printf ( "\nname = %s phone = %s", e.name, e.a.phone ) ;
printf ( "\ncity = %s pin = %d", e.a.city, e.a.pin ) ;
}

And here is the output...
name = jeru phone = 531046
city = nagpur pin = 10
Notice the method used to access the element of a structure that is part of another structure. For this the dot operator is used twice, as in the expression,
e.a.pin or e.a.city
Of course, the nesting process need not stop at this level. We can nest a structure within a structure, within another structure, which is in still another structure and so on... till the time we can comprehend the structure ourselves. Such construction however gives rise to variable names that can be surprisingly self descriptive, for example:
maruti.engine.bolt.large.qty
This clearly signifies that we are referring to the quantity of large sized bolts that fit on an engine of a maruti car.
(c) Like an ordinary variable, a structure variable can also be passed to a function. We may either pass individual structure elements or the entire structure variable at one go. Let us examine both the approaches one by one using suitable programs.
 
/* Passing individual structure elements */
main( )
{
struct book
{
char name[25] ;
char author[25] ;
int callno ;
} ;
struct book b1 = { "Let us C", "YPK", 101 } ;
display ( b1.name, b1.author, b1.callno ) ;
}
display ( char *s, char *t, int n )
{
printf ( "\n%s %s %d", s, t, n ) ;
}

And here is the output...
Let us C YPK 101
Observe that in the declaration of the structure, name and author have been declared as arrays. Therefore, when we call the function display( ) using,

display ( b1.name, b1.author, b1.callno ) ;

we are passing the base addresses of the arrays name and author, but the value stored in callno. Thus, this is a mixed call—a call by reference as well as a call by value.
It can be immediately realized that to pass individual elements would become more tedious as the number of structure elements go on increasing. A better way would be to pass the entire structure variable at a time. This method is shown in the following program.
 
struct book
{
char name[25] ;
char author[25] ;
int callno ;
} ;

main( )
{
struct book b1 = { "Let us C", "YPK", 101 } ;
display ( b1 ) ;
}
display ( struct book b )
{
printf ( "\n%s %s %d", b.name, b.author, b.callno ) ;
}

And here is the output...
Let us C YPK 101

Note that here the calling of function display( ) becomes quite compact,
display ( b1 ) ;
Having collected what is being passed to the display( ) function, the question comes, how do we define the formal arguments in the function. We cannot say,
struct book b1 ;
because the data type struct book is not known to the function display( ). Therefore, it becomes necessary to define the structure type struct book outside main( ), so that it becomes known to all functions in the program.
(d) The way we can have a pointer pointing to an int, or a pointer pointing to a char, similarly we can have a pointer pointing to a struct. Such pointers are known as ‘structure pointers’.
Let us look at a program that demonstrates the usage of a structure pointer.
main( )
{
struct book
{
char name[25] ;
char author[25] ;
int callno ;
} ;
struct book b1 = { "Let us C", "YPK", 101 } ;
struct book *ptr ;
ptr = &b1 ;
printf ( "\n%s %s %d", b1.name, b1.author, b1.callno ) ;
printf ( "\n%s %s %d", ptr->name, ptr->author, ptr->callno ) ;
}
The first printf( ) is as usual. The second printf( ) however is peculiar. We can’t use ptr.name or ptr.callno because ptr is not a structure variable but a pointer to a structure, and the dot operator requires a structure variable on its left. In such cases C provides an operator ->, called an arrow operator to refer to the structure elements. Remember that on the left hand side of the ‘.’ structure operator, there must always be a structure variable, whereas on the left hand side of the ‘->’ operator there must always be a pointer to a structure. The arrangement of the structure variable and pointer to structure in memory is shown in the Figure



Can we not pass the address of a structure variable to a function? We can. The following program demonstrates this.
 
/* Passing address of a structure variable */
struct book
{
char name[25] ;
char author[25] ;
int callno ;
} ;
main( )
{
struct book b1 = { "Let us C", "YPK", 101 } ;
display ( &b1 ) ;}
display ( struct book *b )
{
printf ( "\n%s %s %d", b->name, b->author, b->callno ) ;
}
And here is the output...
Let us C YPK 101
Again note that to access the structure elements using pointer to a structure we have to use the ‘->’ operator.
Also, the structure struct book should be declared outside main( ) such that this data type is available to display( ) while declaring pointer to the structure.
(e) Consider the following code snippet:
struct emp
{
int a ;
char ch ;
float s ;
} ;
struct emp e ;
printf ( "%u %u %u", &e.a, &e.ch, &e.s ) ;
If we execute this program using TC/TC++ compiler we get the addresses as:
 
65518 65520 65521
 
As expected, in memory the char begins immediately after the int and float begins immediately after the char.
However, if we run the same program using VC++ compiler then the output turns out to be:
 
1245044 1245048 1245052

It can be observed from this output that the float doesn’t get stored immediately after the char. In fact there is a hole of three bytes after the char. Let us understand the reason for this. VC++ is a 32-bit compiler targeted to generate code for a 32-bit microprocessor. The architecture of this microprocessor is such that it is able to fetch the data that is present at an address, which is a multiple of four much faster than the data present at any other address. Hence the VC++ compiler aligns every element of a structure at an address that is multiple of four. That’s the reason why there were three holes created between the char and the float.
However, some programs need to exercise precise control over the memory areas where data is placed. For example, suppose we wish to read the contents of the boot sector (first sector on the floppy/hard disk) into a structure. For this the byte arrangement of the structure elements must match the arrangement of various fields in the boot sector of the disk. The #pragma pack directive offers a way to fulfill this requirement. This directive specifies packing alignment for structure members. The pragma takes effect at the first structure declaration after the pragma is seen. Turbo C/C++ compiler doesn’t support this feature, VC++ compiler does. The following code shows how to use this directive.
 
#pragma pack(1)
struct emp
{
int a ;
char ch ;
float s ;
} ;

#pragma pack( )
struct emp e ;
printf ( "%u %u %u", &e.a, &e.ch, &e.s ) ;

Here, #pragma pack ( 1 ) lets each structure element to begin on a 1-byte boundary as justified by the output of the program given below:

1245044 1245048 1245049

No comments:

Post a Comment