Lecture 13:  Structures and Unions

 

Objectives of this lecture

q       Learn how to declare and use structures

q       Learn how to pass structures as parameters to functions

q       Differentiate between arrays and structures

q       Learn how to declare and use Unions

 

How to declare and initialize structures

q       Recall that array is used to store homogeneous data elements.

q       However, many real - life entities have heterogeneous data elements.  For example, a student Id_no. may be int, his name char, and his grade float.

q       The struct (structure) specifier is used to declare a data structure that can store heterogeneous data elements.

q       The elements can be of any type including enumerated types, arrays and even other structures.

E.g:   struct user_record {int id_no;

                                              STRING name;

                                              STRING dept;

                                              int no_of_books;

                                              USER_STATUS status;

                                           };

 

q       This declares a structure with user_record as tag.  Again, memory is not allocated until a variable is declared.

q       A variable with the above structure can be declared as:

struct user_record user;

 

q       We can simplify the variable declaration above, if we use typedef in the declaration.  The above can then be defined as:

typedef struct { int id_no;

                             STRING name;

                             STRING dept;

                             int no_of_books;

                             USER_STATUS status;

                      } USER_RECORD;

 

q       The variable declaration then becomes:

USER_RECORD user;

q       Once a variable is declared, we can access the individual elements (called fields) of the record using the member operator, also called the dot (.) operator.

q       For example, the following program reads and prints values for the structure variable user:

 

#include <stdio.h>

#include <string.h>

 

typedef char STRING[81];

typedef enum {STUDENT, STAFF, FACULTY} USER_STATUS;

typedef struct {int id_no;

                   STRING name;

                   STRING dept;

                   int no_of_books;

                   USER_STATUS status;

                 } USER_RECORD;

 

main()

{  USER_RECORD user;

 

   user.no_of_books=0;

   printf("Enter user details:\n");

   printf("ID_NO: ");  scanf("%d",&user.id_no);

   printf("NAME: ");  fflush(stdin); gets(user.name);

   printf("DEPARTMENT: ");  gets(user.dept);

   printf("STATUS [0 for student, 1 for staff or 2 for faculty: ");

   scanf("%d",&user.status);

 

   printf("\nThe details you entered are:\n");

   printf("ID_NO:      %d\n", user.id_no);

   printf("NAME:       %s\n", user.name);

   printf("DEPARTMENT: %s\n", user.dept);

   printf("STATUS:     %d\n", user.status);

   printf("NO OF BOOKS %d\n", user.no_of_books);

  

    return 0;

}

 

q       Different structures (in the same program) can have the same component name. A structure component name can also be the same as an ordinary variable name – WHY?

 

 

Nested (or compound) Structure:

q       A structure can have as its field another structure.

E.g.   typedef struct { int     day;

                                       int    month;

                                       int    year;

                                   }  DATE;

 

         typedef struct {  int     bk_no;

                                        STRING author;

                                        STRING title;

                                        STRING  publisher;

                                        DATE pub_date;

                                        BOOK_STATUS status;

                                        int edition

     } BOOK_RECORD;

 

      BOOK_RECORD book;

 

q       Notice that we need two dot operators to access the publication date in the variable book as shown below:

book.pub_date.day=2;

book.pub_date.month=5;

book.pub_date.year=1980;

 

Array of structures:

q       We can also have an array of structures. For example, records of books in a small library of 100 books may be stored in the array books declared as follows:

BOOK_RECORD books[100];

 

q       Assignment to the id_no field of the first book in the array can be done as follows:

Books[0].bk_no=3333;

 

Assignment and Comparison among structures:

q       Like simple variables , and unlike arrays, it is allowed to assign an entire structure to another structure of the same type.

e.g.  BOOK_RECORD book1, book2;

          . . . . .  . . . . . . .            /* read values for book1 */

          book2 = book1.

 

q       However, comparison is not allowed on entire structures.  It has to be done on field by field basis.

 

Thus     if (book1 = = book2)   /* is not allowed */

                        . . .  . . . . . 

q       The following function checks if two books are the same.

int samebook (BOOK_RECORD book1,  BOOK_RECORD book2)

{   int  samebook;

     samebook = book1.bk_no == book2.bk_no;

     return samebook;

}

 

Structures as output parameters and the -> operator.

q       Structures can also be passed as output parameters in a manner similar to simple variables, using the * operator.

Example1:  The following function updates the record of a user and a book when the user borrows the book from our small library.

void  update_records( BOOK_RECORD *book,

   USER_RECORD *user)

{   (*book).status = BORROWED;

     (*user).no_of_books++;

}

 

q       In the above example, the * is necessary since book in the function is a pointer variable, so we need to dereference it to  access the object it is pointing to.

q       The bracket in (*book).status is also necessary since the dot operator has high priority than *.

q       Another way to represent above statement is by using the arrow operator (->).  E.g.

book -> status;  is equivalent to   (*book).status;

book->pub_date.day; is equivalent to (*book).pub_date.day;

 

Example2:  The following function re-write the program that reads and prints the user record such that the reading is done by a function.

#include <stdio.h>

#include <string.h>

 

typedef char STRING[81];

typedef enum {STUDENT, STAFF, FACULTY} USER_STATUS;


typedef struct {int id_no;

                             STRING name;

                             STRING dept;

                             int no_of_books;

                             USER_STATUS status;

                          } USER_RECORD;

 

void get_user_details(USER_RECORD *user)

{    (*user).no_of_books=0;

      printf("Enter user details:\n");

      printf("ID_NO: ");  scanf("%d",&(*user).id_no);

      printf("NAME: ");  fflush(stdin); gets((*user).name);

      printf("DEPARTMENT: ");  gets((*user).dept);

      printf("STATUS [0 for student, 1 for staff or 2 for faculty:");

      scanf("%d",&(*user).status);

}

 

main()

{  USER_RECORD user;

 

   get_user_details(&user);

 

   printf("\nThe details you entered are:\n");

   printf("ID_NO:      %d\n", user.id_no);

   printf("NAME:       %s\n", user.name);

   printf("DEPARTMENT: %s\n", user.dept);

   printf("STATUS:     %d\n", user.status);

   printf("NO OF BOOKS %d\n", user.no_of_books);

 

   return 0;

}

 

Arrays versus structures:

q       Both arrays and structures can store more than one data elements.

q       Arrays elements and structure components are contiguously laid out in memory

q       Arrays are homogeneous, structures can be heterogeneous

q       Arrays are implicitly passed to functions by reference while structures have to be explicitly passed by reference

q       Arrays cannot be return by a function using the return statement but structures can

q       Arrays cannot be passed by value but structures can

q       Arrays cannot be assigned to each other using assignment statement but structures can

q       Array identifiers can be compared while structure identifiers cannot

 


Unions

q       Union like struct is a derived type.  Its declaration is similar to that of syntax but its elements share the same memory storage.

                        e.g.  union int_or_float

        {  int i;

            float f;

         }

 

q       In this declaration, int_or_float  is the tag while i and f are the members of the union.

q       A variable of type union int_or_float can be created as in:

union int_or_float a, b, c;

q       This allocates storage for the identifiers a, b, and c.  For each variable, the compiler allocates enough storage to accommodate the largest of the specified members.

q       The notation used to access a member of a union is the same as that used to access structures.

q       The typedef specifier can also be used to simplify the declaration of union: 

e.g.      typedef union

                        {  int i;

                            float f;

                        }NUMBER;

main()

{   NUMBER n;

     n.i = 4444;

     printf(“i: %d   f: %f\n”,n.i, n.f);

     n.f=4444.0;

     printf(“i: %d   f: %f\n”,n.i, n.f);

     return 0;

}

 

Sample output:

i:  4444                f:  0.0000000

i: -8192               f:  4444.0000