HIGHER LEVEL TYPES 2 |
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. Two C examples are presented in Tables 1 and 2.
#include |
Table 1: C enumerated type example 1
#include |
Table 2: C enumerated type example 2 (with "typedef" statement)
Ada provides a number of attributes (constants) for enumerations:
First | Gives first value of the enumeration |
---|---|
Last | Gives 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). An Ada example of the application of enumeration attributes is presented in Table 3.
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; |
Table 3: Ada enumerated type example (illustarting use of attributes)
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.
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.
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.
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.
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.
with CS_IO ; use CS_IO ; procedure RECORD_EXAMPLE_1 is type DATE_T is record DAY: integer; MONTH: string(1..9); YEAR: integer; end record; BIRTHDAY: DATE_T := (15,"May ",1993); begin putline("Birthday = "); put(BIRTHDAY.DAY); put(" "); put(BIRTHDAY.MONTH); put(BIRTHDAY.YEAR); new_line; end RECORD_EXAMPLE_1 ; |
Table 4: Ada record example 1
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 ; |
Table 5: Ada record example 2
#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); } |
Table 6: C structure example
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.
It is often necessary, given a particular application, to create a number of records/structures of the same type in which to store data, e.g. an array of structures.
#include < stdio.h> #include < string.h> typedef struct date { int day; char month[9]; int year; } DATE_T, *DATE_PTR_T; /* MAIN */ void main(void) { int numOfEl; DATE_T structArray[3]; /* Load array */ assignToStructure(structArray,0,15,"October",1991); assignToStructure(structArray,1,15,"May",1993); assignToStructure(structArray,2,9,"August",1997); /* Output array */ output(structArray,0); output(structArray,1); output(structArray,2); } /* ASSIGN TO STRUCTURE */ void assignToStructure(DATE_PTR_T newArray, int index, int newDay, char *newMonth, int newYear) { newArray[index].day = newDay; strcpy(newArray[index].month,newMonth); newArray[index].year = newYear; } /* OUTPUT */ void output(DATE_PTR_T newArray, int index) { printf("%d %s %d\n",newArray[index].day,newArray[index].month, newArray[index].year); } |
Table 7: C array of stuctures example
In many cases the number of elements in such an array are known in advance, and consequently the amount of memory required is known in advance. We refer to such an array as a static array of structures/records. In other cases we do not know how many elements are to be in the array until run time, i.e. a dynamic array of structures/records. In this case we must create sufficient memory at run time. 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. An example is presented in Table 8.
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; |
Table 8: Use of Ada allocators 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.
Table 9 gives some C example code where we create a dynamic array of structures.
#include < stdio.h> #include < string.h> typedef struct date { int day; char month[9]; int year; } DATE_T, *DATE_PTR_T; void main(void) { int numOfEl; DATE_PTR_T structArray; printf("Enter number of structures "); scanf("%d",&numOfEl); if ((structArray = (DATE_PTR_T) (malloc(sizeof(DATE_T) * numOfEl))) == NULL) { printf("Insufficient space\n"); exit(-1); }--- Rest of code here --- |
Table 9: Create a dynamic array of structure in C
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.
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.
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; 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; begin CREATE_BIRTHDAY_RECORD(15,"October ", 1991,BIRTHDAYPTR); CREATE_BIRTHDAY_RECORD(15,"May ", 1993,BIRTHDAYPTR); CREATE_BIRTHDAY_RECORD(21,"April ", 1957,BIRTHDAYPTR); end ADA_LINKED_LIST_EX ; |
Table 10: Simple Ada linked list example.
#include < stdio.h> #include < stdlib.h> typedef struct date { int day; char month[9], int year; struct date *next; } DATE_T, *DATE_PTR_T; DATE_PTR_T createBdayStruct(int, int, int); void main(void) { DATE_PTR_T birthdayPtr=NULL, newPtr=NULL; birthdayPtr=createBdayStruct(15,"October",1991); newPtr=createBdayStruct(15,"May",1993); newPtr->next=birthdayPtr; birthdayPtr=newPtr; newPtr=createBdayStruct(21,"April",1957); newPtr->next=birthdayPtr; birthdayPtr=newPtr; } DATE_PTR_T createBdayStruct(int day, int month, int year) { DATE_PTR_T newPtr; if ((newPtr = (DATE_PTR_T) (malloc(sizeof(DATE_T))))==NULL) { printf("Insufficient space\(bsn"); exit(-1); } newPtr->day = day; strcpy(newPtr->month,month); newPtr->year = year; newPtr->next = NULL; return(newPtr); } |
Table 11: Simple C linked list example.
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 ; |
Table 12: More comlex Ada linked list example.
Note that in the above code it would be better to step through the linked list using some form of loop construct.
#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); } |
Table 13: More complex C linked list example.
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:
#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); } |
Table 14: More complex C linked list example.
Note that:
Unions are not supported by Ada and Pascal.
In principle all of the above type constructors can be combined. For example we can arrays of structures, structures contaoning arrays and so on. In practice, however, each langauge has its own set of restrictions dictated (usually) by implementaion considerations. Two features, X and Y, which can be combined freely, without in anyway restricting each other, are said to be orthogonal. The term is derived from a metaphore in which the two featues are ploted on a 2-D graph such that movement along the X axis does not effect instances of Y and vice-versa (see Bal and Grune (1994) for more detail). Orthogonality is a very strong guiding principle in language design, which was first used explicitly in the design of Algol 68.
We can identify three types of orthogonality:
Combination orthogonality states that if one kind of Y can be combined with one kind of X than any kind of Y can be combined with any kind of X.
For example in we can identify three kinds of declaration:
We can also identify many different kinds of types: int, char, arrays, structures and so on. Combination orthogonality dor declarations and types states that any kind of declaration can be combined with any kind of type. This sort of orthogonality is violated in a number of places in C (e.g. we cannot initialise unions) although is well supported in Ada.
Sort orthogonality states that in any circumstance in which one kind of X is allowed to be meaningfully combined with a particular Y any kind of X is allowed to be meaningfully combined with that particylar Y. This is a special case of combination orthogonality (where one axis in the metaphore is kept constant). Sort orthogonality for types implies that (e.g.) where structures may contain basic types they should alos be allowed to contain higher level types.
Number orthogonality states that in any circumstance in which one X is allowed to be meaningfully combined with Y, zero or more than one X can also be meaningfully combined Y. This implies, for example, that where one declartaion is allowed, zero or more than one should also be allowed.
Return to imperative home page or continue.
Created and maintained by Frans Coenen. Last updated 03 July 2001