HIGHER LEVEL DATA TYPES 2

Select your favourite washing powder: (1) Enumerated types, (2) Records/Structures, (3) Linked Lists or (4) Unions.

CONTENTS



ENUMERATED TYPES

An enumerated type is a user defined scaler type where the possible values for the type are itemised. Ada example declaration:

type DAYS is (MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY);
NEWDAY: DAYS:= WEDNESDAY;

Here we have created an "enumerated" type DAYS and an instance, NEWDAY of that type. Note that the values in an enumerated type are "ordered" so that the value listed first is least and the value listed last is greatest. Conceptually at least, each value for an enumeration also has an ordinal value. This allows comparison of enumerated type values using operators such as < (less than) and > (greater than). In C simple arithmetic can be performed on the basis of such values. C example:

#include 

void main(void)
{
enum days {monday=1, tuesday, wednesday, thursday, friday, saturday, sunday};
enum days newDay = monday;
int n;

scanf("%d",&n);

printf("newDay      = %d\n",newDay);
newDay = monday+n;
printf("newDay  + %d = %d\n",n,newDay);
}

Input integer (between 0 and 6) num and .


Ada Attributes for Enumerated Types

Ada provides a number of attributes (constants) for enumerations:

FirstGives first value of the enumeration
LastGives last value of the enumeration
pred(X)Gives the predecessor of the enumerated value X
succ(X)Gives the successor of the enumerated value X

Note that the value used in conjunction with the pred and succ attributes must not be the first or last values respectively. Some imperative languages that support the constant/attribute concept include an ord(X) attribute which returns the ordinal value of X (Pascal supports this). Ada example of the application of enumeration attributes:

with TEXT_IO ; use TEXT_IO ;

procedure ENUMERATION_ATTRIBUTES is
        type DAYS_T is (MONDAY, TUESDAY, WEDNESDAY,
                THURSDAY, FRIDAY, SATURDAY, SUNDAY);
        package DAYS_INOUT is new enumeration_io(DAYS_T);
        use DAYS_INOUT;
        NEWDAY: DAYS_T;

begin
        put("DAYS_T'FIRST:= ");put(DAYS_T'FIRST); new_line;
        put("DAYS_T'LAST:= ");put(DAYS_T'LAST); new_line;
        get(NEWDAY);
        put("NEWDAY:= ");put(NEWDAY);new_line;
        put("DAYS_T'PRED(NEWDAY):= ");
        put(DAYS_T'PRED(NEWDAY)); new_line;
        put("DAYS_T'SUCC(NEWDAY):= ");
        put(DAYS_T'SUCC(NEWDAY)); new_line;
        if (NEWDAY < THURSDAY) then
                put("if NEWDAY < THURSDAY, NEWDAY:= ");
                put("MONDAY, TUESDAY or WEDNESDAY");
                new_line;
        end if;
end ENUMERATION_ATTRIBUTES;

Further Examples of Enumerated Types

A well known enumerated types (although not often recognised as such) is the Boolean type. This may be specified in Ada and C respectively as follows:

type BOOLEAN_T is (FALSE, TRUE);

typedef enum {false, true} BOOLEAN_T;

NOTE: the type boolean is a pre-defined (enumerated) type in some imperative languages (Ada, Pascal).

It is also possible to consider any discrete type (e.g. integer, character) to be an enumerated type.




RECORDS/STRUCTURES

Arrays are used to group together groups of data items of the same type. A record (Ada) or structure (C) is used to group together a "fixed" number of data items (not necessarily of the same type) into a single data item. The items in a record are called fields or components (or sometimes members). Unlike arrays or enumerated types records can be recursive, i.e. they can contain fields of the same type as the record in which the field is contained.

Accessing Components of Records/Structures

The fields of a record/structure are accessed using the record name and the field name (used in this way the latter is referred to as a selector), linked by a member operator. In Ada, Pascal and C this operator is a `.' (dot). However, in C, if a pointer to a structure is used instead of its name an "arrow" (->) operator is used.

Given a "nesting" of records a number of member operators and selectors can be chained together. Some languages (Pascal) support a with mechanism which can be used to avoid repeated use of pairs of member operators and selectors.

Assignment to Records/Structures

In some imperative languages (C) each member of a structure must be assigned a value individually. Other imperative languages (Ada and Pascal) support the use of aggregates.

Comparing Records/Structures

Some imperative languages (Ada, Pascal) provide an operator to carry out comparison of records (Ada uses the = and /= operators). C does not support such comparison.

In the following two examples we create a date record type and two instances of that record (christmas and birthday), assign values to the fields for each of the created records and then output those values by accessing the records.

Ada Record Example

with CS_IO ; use CS_IO ;

procedure RECORD_EXAMPLE is
        type DATE_T is record
                DAY   : integer;
                MONTH : string(1..10);
                YEAR  : integer;
        end record;
        NUMBER        : integer;
        INPUT_STRING  : string(1..10);
        CHRISTMAS     : DATE_T := (25,"December  ",1996);
        BIRTHDAY      : DATE_T;

        -- OUTPUT RECORD PROCEDURE
                procedure OUTPUT_RECORD(RECID: DATE_T) is
                begin
                        put(RECID.DAY); put(" ");
                        put(RECID.MONTH(1..10));
                        put(RECID.YEAR); new_line;
                end OUTPUT_RECORD;

-- TOP LEVEL PROCEDURE

begin
        put("Christmas = ");
        OUTPUT_RECORD(CHRISTMAS);

        get(BIRTHDAY.DAY);
        get_line(BIRTHDAY.MONTH,NUMBER);
        get(BIRTHDAY.YEAR);

        put("Birthday = ");
        OUTPUT_RECORD(BIRTHDAY);
end RECORD_EXAMPLE ;

Input integer for DAY , input string for MONTH , and input integer for YEAR , and then .

C Structure Example

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

typedef struct date {
     int day;
     char month[9];
     int year;
     } DATE_T, *DATE_PTR_T;

void output_structure(DATE_PTR_T);

void main(void)
{
DATE_T stPatricksDay;
DATE_T birthday;

/* Saint Patrick's day. */

stPatricksDay.day = 17;
strcpy(stPatricksDay.month,"March");
stPatricksDay.year = 1996;

output_structure(&stPatricksDay);

/* Birthday. */

scanf("%d",&birthday.day);
scanf("%s",birthday.month);
scanf("%d",&birthday.year);

output_structure(&birthday);
}

void output_structure(DATE_PTR_T datePtr)
{
printf("%d ",datePtr->day);
printf("%s %d\n",datePtr->month,datePtr->year);
}

Input integer for DAY , input string for MONTH , and input integer for YEAR , and then .


Variant Records

It is sometimes useful to be able to specify a number of alternative fields for a record, this is referred to as a variant record. Such records are a feature of imperative languages such Pascal and Modula-2 (but not Ada or C). A variant record comprises a fixed part, with fields as in a normal record, and a variant part, in which alternative fields are specified. The group of alternative fields which are applicable in any given instance of the record type is indicated by the value assigned to a tag field which is contained in the fixed part of the record.




LINKED LISTS

One of the most useful applications of records (structures) is to define organisations of data in which distinct items are linked together using pointers/access values. The simplest general form of a group of linked records is a linked list (more complex forms include trees of various types). In a linked list each record includes a pointer to a following record. The final pointer is a null pointer (defined using the reserved word NULL in both Ada and C) indicating that the last record in the linked list has been reached.

A LINKED LIST

The standard mechanism for processing linked lists is to step through the list, using some form of loop construct, until the terminating null pointer is reached.


Static and Dynamic Records Structures

In some cases the memory required to store data in a record is known in advance. Such records/structures are said to be static. More often than not we do not know what memory is required until run time. In this case the record/structure is described as being dynamic. This is often the case when using linked lists.


ALLOCATORS

To create a dynamic data item (i.e. during run time) we must use an allocator which reserves space in memory for the data item. Examples

Ada allocator:

new integer

C allocator:

malloc(sizeof(integer))

here we have dynamically created space for an integer type. An allocator (such as new or malloc) returns a reference (address), referred to as an access value in Ada and a pointer in C, which indicates the first byte of the allocated memory.

In Ada, in addition to reserving memory for a particular type, it is also possible to add initialisation terms to the allocator so that a newly created object can be given a value. This is achieved using an "apostrophe" operator which is inserted between the new type name and the desired value. For example:

PRESS
with CS_IO; use CS_IO;

procedure ALLOCATOR_EXAMPLE is
        type INT_PTR_T is access INTEGER;
        PI: INT_PTR_T;

begin
        PI:= new integer'(2);
        put("PI.all = ");put(PI.all);
        new_line;
end ALLOCATOR_EXAMPLE;

Here we have created an access value for an integer type. Note the use of the "dot" (member) operator. In the following (comprehensive) examples we will created a linked list of date records of the form defined earlier.


Comprehensive Ada Linked List Example

PRESS
with CS_IO ; use CS_IO ;

procedure ADA_LINKED_LIST_EX is
        type DATE_T;    -- Incomplete declaration
        type DATEPTR_T is access DATE_T;
        type DATE_T    is  record
                DAY  : integer; MONTH : string(1..9);
                YEAR : integer; NEXT  : DATEPTR_T;
                end record;
        BIRTHDAYPTR: DATEPTR_T := null;

        --------------------------------------------------------------------
        -- CREATE_BIRTHDAY_RECORD
        -- Procedure to create a single record of type DATA-T
        procedure CREATE_BIRTHDAY_RECORD (DAY: in integer; MONTH: in string;
                YEAR:   in integer; NEWPTR: in out DATEPTR_T) is
        begin
                NEWPTR := new DATE_T'(DAY,MONTH,YEAR,NEWPTR);
        end CREATE_BIRTHDAY_RECORD;
        --------------------------------------------------------------------
        -- OUTPUT
        -- Procedure to output a single record
        procedure OUTPUT (LINKPTR: in DATEPTR_T) is
        begin
                put("Birthday = ");
                put(LINKPTR.DAY, width => 2);
                put(" ");put(LINKPTR.MONTH(1..9));
                put(LINKPTR.YEAR, width => 3);new_line;
        end OUTPUT;
        --------------------------------------------------------------------
        -- OUTPUT_BIRTHDAY_RECORD
        -- Procedure to output contents of a single record of type DATA-T
        procedure OUTPUT_BIRTHDAY_RECORDS (STARTPTR: in DATEPTR_T) is
                LINKPTR : DATEPTR_T := STARTPTR;
        begin
                OUTPUT(LINKPTR); LINKPTR := LINKPTR.NEXT;
                OUTPUT(LINKPTR); LINKPTR := LINKPTR.NEXT;
                OUTPUT(LINKPTR);
        end OUTPUT_BIRTHDAY_RECORDS;
        --------------------------------------------------------------------

begin
        CREATE_BIRTHDAY_RECORD(15,"October  ",1991,DATE_PTR_T);
        CREATE_BIRTHDAY_RECORD(15,"May      ",1993,DATE_PTR_T);
        CREATE_BIRTHDAY_RECORD(21,"April    ",1957,DATE_PTR_T);

        OUTPUT_BIRTHDAY_RECORD(DATE_PTR_T);
end ADA_LINKED_LIST ;

Note that in the above code it would be better to step through the linked list using some form of loop construct.


Comprehensive C Linked List Example

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

/* ------ DEFINE GLOBAL VARIABLES ------- */

/* Structure: */

typedef struct date {
          int day, month, year;
          struct date *next;
          } DATE_T, *DATE_PTR_T;

/* ------ FUNCTION PROTOTYPES ------ */

DATE_PTR_T createBirthdayStruct(int, int, int);
void outputBirthdayStruct(DATE_PTR_T);
void output(DATE_PTR_T linkPtr);

/* -------------------- */
/*                      */
/*         MAIN         */
/*                      */
/* -------------------- */

void main(void)
{
DATE_PTR_T birthdayPtr = NULL, newPtr = NULL;

/* Create first structure. */

birthdayPtr = createBirthdayStruct(15,10,91);

/* Create second structure. */

newPtr = createBirthdayStruct(15,5,93);
newPtr->next = birthdayPtr;
birthdayPtr = newPtr;

/* Create third structure. */

newPtr = createBirthdayStruct(21,4,57);
newPtr->next = birthdayPtr;
birthdayPtr = newPtr;

/* Outout linked list */

outputBirthdayStruct(birthdayPtr);
}

/* ------ CREATE BIRTHDAY STRUCTURE ------ */

/* Create single structure of type DATE_T and return a pointer to it. */

DATE_PTR_T createBirthdayStruct(int day, int month, int year)
{
DATE_PTR_T newPtr;

if ((newPtr = (DATE_PTR_T)(malloc(sizeof(DATE_T))))==NULL) {
        printf("Insufficient storage space\n");
        exit(-1);
        }

newPtr->day = day;
strcpy(newPtr->month,month);
newPtr->year = year;
newPtr->next = NULL;

return(newPtr);
}

/* ------ OUTPUT BIRTHDAY STRUCTURE ------ */

/* Output linked list of structures of type DATE_T. */

void outputBirthdayStruct(DATE_PTR_T birthdayPtr)
{
DATE_PTR_T linkPtr = birthdayPtr;

output(linkPtr);
linkPtr = birthdayPtr->next;
output(linkPtr);
linkPtr = birthdayPtr->next->next;
output(linkPtr);
}

/* ------ OUTPUT ------ */

/* Output single structure of type DATE_T. */

void output(DATE_PTR_T linkPtr)
{
printf("Birthday = %d/%d/%d\n",linkPtr->day,linkPtr->month,linkPtr->year);
}



UNIONS

It is sometimes desirable to define a variable which can be of two or more different types according to different circumstances (overloading). In imperative languages such a data item is termed a union. The distinction between a union and a structure is that the members of a structure define different variables while the members of a union define different manifestations of the same variable. Thus space need only be allocated to accommodate the largest type specified in a union. C example:

PRESS
#include <stdio.h>

void main(void)
{
typedef union data {
        int integer;
        char character;
        } DATA_T;
DATA_T input;

input.integer = 2;
printf("input.integer = %d\n",input.integer);

input.character = 'A';
printf("input.character = %c\n",input.character);
}

Note that:

Unions are not supported by Ada and Pascal.




CONTINUE YES/NO

CHOOSE YOUR FAVOURITE FABRIC CONDITIONER! (1) Return to imperative home page, or (2) Continue to expressions and statements page.







Created and maintained by Frans Coenen. Last updated 03 July 2001