Ada type declarations are of the form:
< type_name> `is' < type_def>
C type declaration statements are of the form:
`typedef' < type_def & type-name>
The most straight forward (and oldest) form of higher level data type is the array. An array is simply a series of data items, all of the same type, stored in a series of locations in memory such that a single value is held at each location.
When declaring arrays we are doing two things:
The form of these declarations and the versatility with which indexes can be specified is very much language dependent. Broadly we can identify a number of methods of defining indexes:
To illustrate the above it is useful to consider some example code fragments:
In Ada, to declare arrays, we use a declaration statement of the form:
NAME `: array' <index_definition> `of' TYPE ';'
The keyword array used here is often referred to as a type constructor in that it "constructs" a particular type. Indexes in Ada can then be defined in a number of ways as follows:
`(' LOWER_BOUND `..' UPPER_BOUND `)'Example declarations:
ARRAY1 : array (1..9) of integer; START: integer; ARRAY2 : array (START..START+4) of integer;
`(' INDEX_TYPE `range' LOWER_BOUND `..' UPPER_BOUND `)'Example declaration:
ARRAY3 : array (CHARACTER range a..z) of integer;
`(' INDEX_TYPE `)'Example declaration:
type ITEMS_T is range 0..100; ARRAY4 : array (ITEMS_T) of integer;
In C the facilities with which to declare arrays are much more succinct/limited compared to Ada. Array indexes are always assumed to be integers, and the lower bound is always fixed at 0. Format:
TYPE NAME `[' UPPER_BOUND `]' `;'
Example declaration:
int array1[10];
This declares an array of ten elements with indexes ranging from 0 to 9 inclusive. (Declaration assumes index is an integer type with the lower bound fixed at 0.)
Given knowledge of the index for an array element we can assign a value to that element (or change its value) using an "assignment" operation. Ada and C example assignments:
LIST1(1):= 1; list1[0] = 1;
Some imperative languages support the concept of compound values which allow all the elements of an array to be assigned to simultaneously. Both C and Ada support compound values (in Ada they are called aggregates). Note that in C their usage is limited to initialisation, while in Ada array aggregates can be used in any assignment statement (not just initialisation).
Ada and C Examples statements:
MY_ARRAY : array (0..10) of integer := (9, 8, 7, 6, 5, 4, 3, 2, 1, 0); int myArray[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
Ada also supports an alternative to the above:
MY_ARRAY:= (5, 4, 3, 2, 1, other => 0);
Declaring, initialising and outputting the contents of an array.
with CS_IO; use CS_IO; procedure ARRAY_EXAMPLE is START : integer := 2; IA : array (integer range START..START+4) of integer := (2,4,6,8,10); begin put("Array comprises = "); put(IA(2)); put(", "); put(IA(3)); put(", "); put(IA(4)); put(", "); put(IA(5)); put(", "); put(IA(6)); new_line; end ARRAY_EXAMPLE; |
Note, in the above example, it would have been more succinct to use some kind of loop construct.
#include< stdio.h> void main(void) { int ia[5] = {2,4,6,8,10}; printf("Size of array = %d (bytes)\n",sizeof(ia)); printf("Num elements = %d\n",sizeof(ia)/sizeof(ia[0])); printf("Array comprises = %d, %d,%d, %d, %d\n", ia[0],ia[1],ia[2],ia[3],ia[4]); printf("Address ia[0] = %d\n",&ia[0]); printf("ia = %d\n",ia); } |
Note that if we output the name of the array (without an index) we produce the start address of the area in memory where the array is stored.
Some languages, such as PL/1 (but not Ada or C), allow operations on complete arrays. For example in PL/1 we can write:
IA = 0;
This will make all elements in the array IA equal to 0. The same effect can be achieved in Ada by writing:
IA:= (other => 0);
Alternatively, some imperative languages (Pascal) support assignment of one array to another:
ARRAY1 := ARRAY2 ;
Where ARRAY1 and ARRAY2 are two arrays of precisely the same type (such a capability supported by Ada and pascal, but not C).
A Slice is a sub-array of an array. The process of slicing returns a sub-array (supported by Ada but not C). Thus given:
IA: array (integer range START..START+4) of integer := (2,4,6,8,10);
or
IA: array (integer range START..START+4) of character := ('a', 'b', 'c', 'd', 'e');
The statements
IA_SUB := IA(START+1..START+3)
would assign the sub array (4,6,8) or ('b', 'c', 'd') to the variable IA_SUB.
"Slices" example:
with CS_IO; use CS_IO; procedure EXAMPLE is START: constant := 2; type MY_ARRAY_T is array (integer range START..START+4) of integer; LIST1: MY_ARRAY_T := (2,4,6,8,10); LIST2, LIST3: MY_ARRAY_T; procedure OUTPUT_ARRAY (LIST: MY_ARRAY_T) is begin--- Output array contents --- end OUTPUT_ARRAY; begin OUTPUT_ARRAY(LIST1); LIST2 := LIST1; OUTPUT_ARRAY(LIST2); LIST3 := LIST1(2..4) & LIST2(2..3); OUTPUT_ARRAY(LIST3); end EXAMPLE; |
To be compatible two arrays must be of the same length. This means that a general purpose procedure that use arrays can only work for arrays of a pre-specified size. Thus separate procedures must be written for each possible size of array. One way of getting round this is to define formal parameters for such a general procedure in terms of the maximum and minimum expected bounds for the arrays to be passed to the procedure. As a result any array which has bounds within the range defined by the formal parameters to the procedure can be passed as the actual parameters. Such parameters are referred to as a conformant array parameters. These are a feature of Pascal, but not C and Ada. The latter use pointers or access values to pass arrays of varying length (but same type) to functions/procedures.
Ada supports a number of attributes which can be used with respect to array:
with CS_IO ; use CS_IO ; procedure ARRAY_ATTRIBUTES is START : integer:= 2; IA : array (integer range START..START+4) of character := ('a', 'b', 'b', 'd', 'e'); begin put("IA'first = "); put(IA'first); new_line; put("IA'last = "); put(IA'last); new_line; put("IA'length = "); put(IA'length); new_line; end ARRAY_ATTRIBUTES ; |
A constrained array is an array where the index is specified (and hence the number of components is specified), we say that the bounds are static, hence constrained arrays are sometimes referred to as static arrays. Many imperative languages (including Ada) support the concept of unconstrained arrays. C and Pascal do not, however, C does provide facilities whereby such arrays can be simulated.
Ada makes use of the symbol <> to indicate an unconstrained array:
type TYPE_NAME is array (T1 range < >) of T2; DATA_ITEM_NAME : TYPE_NAME(START..END);
where T1 and T2 are type identifiers (not necesseraily of the same type), and the data items START and END are of type T1.
Example declaration:
type LIST1 is array (INTEGER range < >) of FLOAT; L1 : LIST1(START..END);
Although C does not support the concept of unconstrained arrays it does provide facilities
to delay the declaration of an upper bound of an array till run time, i.e. the upper bound is declared dynamically
hence such an array is referred to as a
Example:
#include < stdlib.h> #include < stdio.h> void main(void) { int num, index = 0; int *numPtr = NULL; /* Get size of array and create space (Malloc returns NULL if no space available). */ scanf("%d",&num); numPtr = (int *) malloc(sizeof(int)*num); if (numPtr == NULL) { printf("Not enough memory\n"); exit(1); } /* Initialise. */ while (index < = num) { numPtr[index] = index; index++; } /* Output. */ index = 0; while (index < = num) { printf("%d ",numPtr[index]); index++; } printf("\n"); /* End. */ free(numPtr); } |
Other than the standard array forms described above we can identify a number of alternative "types" of array which are a feature of particular imperative languages. These include:
A powerful facility associated with some imperative languages is the ability to dynamically "resize" an array after it has been declared, i.e. during run-time. Such an array is referred to as a flexible array. Neither Ada or C support flexible arrays (Algol'68 does). However, a similar effect can be achieved in C using the built-in functions malloc and realloc. The latter dynamically extends or contracts the memory space available for a previously declared array.
C Example:
#include < stdlib.h> #include < stdio.h> int num = 4; void main(void) { int *numPtr = NULL; /* Allocate memory and initialise. */ numPtr = (int *) malloc(sizeof(int)*num); numPtr[0] = 2; numPtr[1] = 4; numPtr[2] = 6; numPtr[3] = 8; /* Output. */--- Ouput code --- /* Reallocate memory and reinitialise. */ num = 3; numPtr = (int *) realloc(numPtr,sizeof(int)*num); numPtr[0] = 1; numPtr[1] = 3; numPtr[2] = 5; /* Output. */--- Ouput code --- /* End */ free(numPtr); } |
Multi-dimensional arrays are arrays where the elements have more than one index. In the case of two-dimensional arrays these can be envisaged as tables comprising a number of rows and column such that the row number represents one index and the column number a second index:
1 | 2 |
3 | 4 |
5 | 6 |
with CS_IO ; use CS_IO ; procedure TWO_DIM_ARRAY is type TWO_D_ARRAY_T is array (1..3, 1..2) of integer; IA: TWO_D_ARRAY_T:= ((1, 2), (3, 4), (5, 6)); begin put(IA(1,1)); put(IA(1,2));new_line; put(IA(2,1)); put(IA(2,2));new_line; put(IA(3,1)); put(IA(3,2));new_line; end TWO_DIM_ARRAY ; |
Note that the "row" index is declared first and then the "column" index.
void main(void) { int twoDarray[3][2] = {{1, 2}, {3, 4}, {5, 6}}; printf("Size of array = %d (bytes)\n",sizeof(twoDarray)); printf("Num elements = %d\n",sizeof(twoDarray)/sizeof(twoDarray[0][0])); printf("Array comprises = (%d, %d), (%d, %d), (%d, %d)\n", twoDarray[0][0],twoDarray[0][1],twoDarray[1][0], twoDarray[1][1],twoDarray[2][0],twoDarray[2][1]); } |
Three dimensional arrays can then be envisaged as comprising a book of tables such that the third index represents page number. Four dimensional arrays can then be envisaged as a set of books of tables and so on.
Some further material on creating dynamic multi-dimensional arrays can be found
here.
Some imperative languages (including Ada and C) support arrays of arrays:
Note that in Ada the "sub-arrays" must all be
of the same type (i.e. size and length), consequently
the distinction between arrays of arrays and two-dimensional arrays can be blurred. Arrays of arrays in C are created in a similar manner. An example is given
here where the technique is used to dynamically create 2-D arrays. Lists (or sequences) can be considered to be special types of arrays but: Lists are popular in logic and functional languages but not in imperative languages. A set is a group of (distinct) elements,
all of the same type, which are all possible values of some other type referred to
as the base type. The relationship is similar to that of an Ada
sub-type to its "super-type".
The distinction between an array and a set is that the elements are not ordered in anyway.
Neither Ada or C feature sets, however Pascal and Modula-2 do. Pascal set
declaration example: Here we have defined a base type SOMEBASE_T and defined a set SOMESET_T
over this base. The number
of elements in a set is referred to as its cardinality.
The only operations that can be performed on sets are set operations, e.g. member, union, intersection etc. Bags (multisets) are similar to sets except that they can contain an element more than
once and record how many times a value has been inserted. The primary operations
on bags are "insert value" and "remove value" (as opposed to the set operations
found in sets).
Very few imperative languages
feature bags A further type of array is the string.
A string is a sequence of characters usually enclosed in double (Ada, C)
or single quotes (Pascal) - for this reason strings are sometimes
referred to as a character arrays. As such we can use standard array operations on strings:
Note also that C strings are peculiar in that the last member of
the sequence must always be the null terminator, written as '\0' (note single quotes). In both of the above examples note the similarity with array declarations. Note that the C strcpy function does not require s1 and s2 to be of
the same length (this is not the case when using the Ada := operator). More on C strings (character arrays) can be found here. Return to imperative home page or
continue. Created and maintained by
Frans Coenen.
Last updated 03 July 2001
Arrays of arrays
with CS_IO ; use CS_IO ;
procedure ARRAY_OF_ARRAY is
type ARRAY_T is array (1..2) of integer;
type TWO_D_ARRAY_T is array (1..3) of A;
A1 : ARRAY_T := (1, 2);
A2 : ARRAY_T := (3, 4);
A3 : ARRAY_T := (5, 6);
IA : TWO_D_ARRAY_T := (A1, A2, A3);
begin
put(IA(1)(1));
put(IA(1)(2));new_line;
put(IA(2)(1));
put(IA(2)(2));new_line;
put(IA(3)(1));
put(IA(3)(2));new_line;
end ARRAY_OF_ARRAY ;
Lists
SETS
program SET_EXAMPLE (output);
type
SOMEBASE_T = 0..10;
SOMESET_T = set of SOMEBASE_T;
var
SET1, SET2 : SOMESET_T;
begin
SET1 := [1, 2, 5];
SET2 := [6, 8, 9];
--- More code in here ---
end.
BAGS
8. STRINGS
String declarations
char name[15];
NAME: string(1 .. 15);
Ada string example programme
with CS_IO ; use CS_IO ;
procedure ADA_STRING is
NAME1: string(1..16);
NAME2: string(1..16);
LENGTH1, LENGTH2: integer;
begin
get_line(NAME1,LENGTH1);
get_line(NAME2,LENGTH2);
put("String length NAME1 = ");
put(LENGTH1);new_line;
put("String length NAME2 = ");
put(LENGTH2);new_line;
put("Input = "); put(NAME1(1..LENGTH1));
put(" and "); put(NAME2(1..LENGTH2));
new_line;
put("Initials = ");
put(NAME1(1)); put(". and ");
put(NAME2(1)); put(".");
new_line;
end ADA_STRING ;
C string example programme
#include < stdio.h>
void main(void)
{
char name1[15], name2[15];
scanf("%s",name1);
scanf("%s",name2);
printf("Input = %s and %s\n",name1,name2);
printf("Initials = %c and %c\n",name1[0],name2[0]);
}
Operations on strings
Operation Ada Pascal C
concatenation S1&S2 strcat(s1,s2)
comparison S1=S2 S1=S2 strcmp(s1,s2)
copy S1:=S2 S1:=S2 strcpy(s1,s2)