First | Next | Previous | Last | Glossary | About |
The data types dealt with so far have been simple data types, i.e. integer, float, character. In many real-world applications data doesn't exist as simple types but as composites of many data types. A student record is an example of a composite. It will contain things like Surname, Firstname, Age, Address and so on. In order to deal with this class of data we need some way of encapsulating several different types of data in a single structure.
In C and C++ we have a number of choices two of which are the struct and the union. A third, the class, forms the basis for object-oriented programs. In this session we look at struct and union.
struct clients { string sname; string fname; float due; };
The keyword struct defines a data type which contains simple or other structured types. The struct word is followed by the struct tag - in this case clients. This is the type definition which is used when we want to declare variables or instances of the struct.
struct clients client;
Given a type clients we can declare variables of that type which declares a variable client of type clients. The type is struct clients, the variable or instance is client.
client.sname client.fname client.due
Any reference to a field of client is made like this, using a dot notation, and each field is treated just as if it was its base type (which of course it is).
Here is an example using a structured data type.
Code | Explanation |
// Sample xx - Structures #include <iostream> #include <string> struct clients { string sname; string fname; float due; }; |
Declare a structure of type clients. It has three fields or members: a string sname, a string fname and a float due. |
int main(void) { struct clients client; client.sname = ""; cout << "Enter client surname: "; cin>> client.sname; cout << client.sname << endl; return 0; } |
declare a variable of type clients:
struct clients client;and set the field sname to an empty string client.sname = "";Get a surname from the user and display it. |
#include <iostream> #include <string> struct clients { string sname; string fname; float due; }; int main(void) { struct clients client[5]; int i; for (i = 0; i < 5; i++) { cout << "Enter client surname: "; cin >> client[i].sname; client[i].fname = "Fred"; client[i].due = 10.00; } for (i = 0; i < 5; i++) cout << client[i].fname << " " << client[i].sname << " owes " << client[i].due << endl; return 0; }
An array of structured types can be declared as shown here and references now include an index. This kind of declaration, arrays of structures, is often used in situations which require sorting structures into some order. For example the array client[] could be sorted in sname order.
The example shown uses a simple for loop to get client surnames from the user of the program then displays each client record.
struct date { int day; int month; int year; }; struct trans { char dtype; float amt; struct date ddate; }; struct record { struct clients master; struct trans detail; };
You have probably realised that the struct can be used as the basis for a database. In structural terms it is not unlike the row of a table in a database like Microsoft's Access.
If you recognise this fact then you probably realise that a database can contain many different kinds of records or structs and that often you will take a number of different kinds of records and present them to a user in a form. The form is a view of the underlying database.
A structure too can be composed of other structured types. Here we have three separate structs. The first, date, is composed of simple types. The second, trans, contains two simple types and the structured type date. The third, record, is composed of the two other structured types.
struct record instance; instance.master.sname instance.master.fname instance.master.due instance.detail.dtype instance.detail.amt instance.detail.ddate.day instance.detail.ddate.month instance.detail.ddate.year
How though do you access all of the members of such a complex structure as a record?
It is done via the chain of field or member names. At the top of the chain is the variable name instance. Since instance has two fields, master and detail these become the next links in the chain.
If we declare a variable of type record (which contains the other structures) then each field can be referenced by its full name using the dot notation shown here.
Code | Explanation |
#include <iostream> #include <string> #include <iomanip> |
|
struct clients { string sname; string fname; float due; }; struct date { int day; int month; int year; }; struct trans { char dtype; float amt; struct date ddate; }; struct record { struct clients master; struct trans detail; }; |
Declare the structures. |
int main(void) { struct record instance; instance.master.sname = "Smith"; instance.master.fname = "John"; instance.master.due = 10.00; instance.detail.dtype = 'D'; instance.detail.amt = 12.00; instance.detail.ddate.day = 1; instance.detail.ddate.month = 10; instance.detail.ddate.year = 1999; |
Initialise the variable. |
cout << setiosflags (ios::fixed | ios::showpoint) << setprecision(2); |
Set the default output formats for the cout stream to fixed point, 2 decimal places. This will show numeric values like $ values. In the next lesson you get to read much more on formatting streams. |
cout << setw(20) << "Surname: " << instance.master.sname << endl; cout << setw(20) << "First name: " << instance.master.fname << endl; cout << setw(20) << "Balance due: " << '$' << instance.master.due << endl; cout << setw(20) << "Transaction type: " << instance.detail.dtype << endl; cout << setw(20) << "Amount: " << '$' << instance.detail.amt << " on " ; cout << instance.detail.ddate.day << '/'; cout << instance.detail.ddate.month << '/'; cout << instance.detail.ddate.year << endl; return 0; } |
Now display the current record. |
struct date { int day; int month; int year; } end_month, end_year; struct trans { char dtype; float amt; struct date ddate; } debtors, creditors; struct record { struc clients master; struc trans detail; } new_acc, edit_acc;
The examples you have dealt with so far all declared structs with a struct tag. You can also declare instances of a struct at the same point that the struct is declared.
Here is an example which shows variables of structured types declared with the structure. The struct variable names follow the struct declaration. The struct tag can be omitted when instance names are used.
Each of the declarations shown above names two variables of its type.
Often this kind of declaration is undesirable since you may want to limit the scope of a struct instance to a particular block of your program.
struct week { int daynum; string dayname; } aweek[7] = { {1,"Sunday"}, {2,"Monday"}, {3,"Tuesday"}, {4,"Wednesday"}, {5,"Thursday"}, {6,"Friday"}, {7,"Saturday"} };
An array of structures can be initialised like any other array. This example shows the initialisation of the daynum and dayname fields in the array aweek.
The union doesn't get much of a mention in many text books, mainly because it seems to have no wide use in applications programming. It certainly gets more use in systems programming and is worth mentioning for that alone.
union combine { char x[4]; char y[4]; }
This is a very simple example of a union declaration. But what does a union do?
It permits two or more fields to occupy the same memory space. In the declaration shown here the fields x and y will actually be addressing the same location in memory.
#include <iostream> union combine { char x[4]; char y[4]; }; int main(void) { union combine zz; char c = 'A'; int i; for (i = 0; i < 4; i++) zz.x[i] = c + i; for (i = 0; i < 4; i++) cout << zz.y[i]; cout << " y occupies the same space as "; for (i = 0; i < 4; i++) cout << zz.x[i]; cout << " x " << endl; return 0; }
This example program shows that the two fields of zz both have the same contents even though only zz.x is initialised.
It is reasonable to wonder about the kinds of programming situations might benefit from the use of a union.
Many desktop database systems use a collection of files as the database rather than a database structure which is stored in a file. Paradox, Dataflex and the XBase systems are good examples. In these systems it is quite common for the data files to contain not only the data records but headers at the start of the data file. The headers contain information about the structure of the records in the file. One simple mechanism for accessing the structures in the file is to use a union. The union can be declared so that it contains a member which represents a header and a member which represents a data base record.
When a datafile is opened the DBMS (Data Base Management System) will read the header or headers and determine where the actual data records start, how big they are, how many there is, how many fields they have and so on. This information then can be used to set up the access to records.
Another scenario I have been involved in required the transfer of file systems from a DOS-based file system to a UNIX-based file system. This required reading data directly from track and sector positions and translating this into file system information. Unions made this very straightforward.
Make sure you try each of the example programs first and make sure they work.
Exercise 2Write a program that uses the structure:
struct clients { string sname; string fname; float due; };
and three separate functions of return type void which get the client surname, firstname and balance due.
If you get stuck there is a sample answer here.
Exercise 3#include <iostream> #include <string> struct clients { string sname; string fname; float due; }; struct clients& getClient(); int main(void) { struct clients client; client = getClient(); cout << client.fname << " " << client.sname << " due " << client.due << endl; return 0; } struct clients& getClient() { struct clients c; c.sname = "Smith"; c.fname = "Fred"; c.due = 10.56; return c; }
Take at look at the program here, what do the following declarations mean?
The program compiles with warnings but crashes when it is executed. Work out why and let me know.
You can run it if you like to see what happens.
struct clients& getClient(struct clients&); ... struct clients& getClient(struct clients& c) { c.sname = "Smith"; c.fname = "Fred"; c.due = 10.56; return c; }
A couple of sessions back, in the lesson on references, I mentioned that functions can return references.
The example here shows how you might do that by adding the reference operator to a type declaration like struct clients&.
You probably agree that it looks little awkward, in fact, since the work on structures you've probably harboured secret thoughts about the verboseness of declaring structures and planned to find better ways than always having to prefix a structure name with the word struct. There is a better way using typedef, meaning type definition.
typedef struct clients { string sname; string fname; float due; } MyClient;
This construction enables the programmer to create synonyms for other data types. In this example MyClient is a type definition for struct clients and in can be used anywhere that struct clients can be used.
There is a sample program here which shows the use of typedef with a struct.
We aren't limited to using typedef just with structures, it can be used with any data type, for example:
but it is commonly used with structs and unions to make programs more readable and cut back on the keyboarding effort.
Write a program which:
to create new MyClient structure.
First | Next | Previous | Last | Glossary | About |
Copyright © 1999 - 2001
David Beech