COMPLEX AND USER DEFINED TYPES
Please note that the material on this website is not intended to be exhaustive.
This is intended as a summary and supplementary material to required textbook.
INTRODUCTION
Previously we have been dealing with C++ built-in types, that is: types that the language already supports. In addition, all of the types we have been using so far (with two exceptions: string, and the reference type: &) have been simple types. In this module we will begin to look at complex types, sometimes called structured types. Complex types allow the programmer to define their own types, which we will call user defined types.
C++ Built-In Types
Classification
Name
Examples
Can be unsigned?
Simple
Integral
char
yes
"
"
short
yes
"
"
int
yes
"
"
long
yes
"
"
bool
no
"
Enumerated
enum
"
"
Decimal
float
"
"
"
double
"
"
"
long double
"
Address
Pointer
* (asterisk)
"
"
Reference
& (ampersand)
"
Complex
Array
[ ] (brackets)
"
"
Structured
struct
"
"
Union
union
"
"
Abstract
class
"
In this module we will explain enumerations, structures, and unions. In future modules we will cover arrays and classes.
Some classifications above may come as a surprise. How can char be an integral type (and even unsigned), and why is bool considered an integral type?
Characters in C++ are considered 8-bit bytes, and hence they are represented internally as integers in base 2: just 0s and 1s. If you want an integer that only ranges between 0 and 255 (unsigned), or between -128 and 127 (signed), you can use a char. All of the arithmetic operations can be used on char.
A character (char) was originally taken to be one of the symbols on your keyboard, and to represent those only need 7 bits are needed. Extended character sets must use 2 or more bytes to represent characters not on your keyboard (such a representation scheme is known as Unicode), for instance, Cyrillic characters or some mathematic symbols.
We have already seen that the Boolean true and false are really internally represented as 1 and 0, respectively. However, arithmetic operations will not work on bool.
You will also notice that a type we have used repeatedly (string) is not listed above. The strings we have been using are really of type class, which will be introduced another module.
ENUMERATIONS
Enumerated types are user-defined; that is, you get to choose what you want to enumerate. Here are some simple examples:
enum PrimaryColors {RED, YELLOW, BLUE}; enum Days {SUN, MON, YUE, WED, THU, FRI, SAT};
C++ will internally represent these types using 0, 1, 2, 3, .... Notice that the standard convention is to use upper-case alphas in the enumeration, as they are really very similar to constants. You can override the internal defaults with something like the following:
enum PrimaryColors {RED = 5, YELLOW = 10, BLUE = 20};
The default internal representation is often very convenient, as the enumeration symbol strings can then be used as indices of arrays, a future topic.
STRUCT.
COMPLEX AND USER DEFINED TYPESPlease note that the material on t.docx
1. COMPLEX AND USER DEFINED TYPES
Please note that the material on this website is not intended to
be exhaustive.
This is intended as a summary and supplementary material to
required textbook.
INTRODUCTION
Previously we have been dealing with C++ built-in types, that
is: types that the language already supports. In addition, all of
the types we have been using so far (with two
exceptions: string, and the reference type: &) have
been simple types. In this module we will begin to look
at complex types, sometimes called structured types. Complex
types allow the programmer to define their own types, which we
will call user defined types.
C++ Built-In Types
Classification
Name
Examples
Can be unsigned?
Simple
Integral
char
yes
"
"
short
yes
"
"
int
yes
"
"
long
3. "
"
Union
union
"
"
Abstract
class
"
In this module we will explain enumerations, structures, and
unions. In future modules we will cover arrays and classes.
Some classifications above may come as a surprise. How
can char be an integral type (and even unsigned), and why
is bool considered an integral type?
Characters in C++ are considered 8-bit bytes, and hence they
are represented internally as integers in base 2: just 0s and 1s.
If you want an integer that only ranges between 0 and 255
(unsigned), or between -128 and 127 (signed), you can use
a char. All of the arithmetic operations can be used on char.
A character (char) was originally taken to be one of the symbols
on your keyboard, and to represent those only need 7 bits are
needed. Extended character sets must use 2 or more bytes to
represent characters not on your keyboard (such a
representation scheme is known as Unicode), for instance,
Cyrillic characters or some mathematic symbols.
We have already seen that the Boolean true and false are really
internally represented as 1 and 0, respectively. However,
arithmetic operations will not work on bool.
You will also notice that a type we have used repeatedly (string)
is not listed above. The strings we have been using are really of
type class, which will be introduced another module.
ENUMERATIONS
Enumerated types are user-defined; that is, you get to choose
what you want to enumerate. Here are some simple examples:
enum PrimaryColors {RED, YELLOW, BLUE}; enum
4. Days {SUN, MON, YUE, WED, THU, FRI, SAT};
C++ will internally represent these types using 0, 1, 2, 3, ....
Notice that the standard convention is to use upper-case alphas
in the enumeration, as they are really very similar to constants.
You can override the internal defaults with something like the
following:
enum PrimaryColors {RED = 5, YELLOW = 10, BLUE =
20};
The default internal representation is often very convenient, as
the enumeration symbol strings can then be used as indices of
arrays, a future topic.
STRUCTURES
Structures are a built-in complex type that also allows the user
to define their own types. For instance, you may want to define
a class list, which will consist of a set of records, one record for
each student in the class. You will want the record to consist of
the following fields (and perhaps other fields as well): first
name, last name, student ID, and course average. For this you
will want to use a structure to represent each student in the
class.
You declare a structure in C++ as follows:
struct TypeName { // list of structure members };
You get to choose the TypeName, and you get to specify the
member list. For our student record example you might declare
a structure of type StudentRecord with 4 members in the
member list:
struct StudentRecord { string firstName;
string lastName; string studentID; float
courseAverage; };
In order to use your structure to hold data on a particular
student, you must declare a variable of type StudentRecord.
(How you deal with the set of these records that represent a
class of students is covered when we discuss arrays.)
StudentRecord record1;
We can proceed to initialize the members of record1 by
5. using dot-notation:
record1.firstName = "John" record1.lastName = "Jones"
record1.studentID = "S0123456789"
record1.courseAverage = 89.6;
We can now use this initialized structure in other programmatic
statements:
cout << "Student Record for: " << record1.firstName <<
' ' << record1.lastName << " ID: " <<
record1.studentID << " Average: " <<
record1.studentAverage << endl;
Note that structures can have structures (but not the same
structure) within them to any finite level of nesting:
struct struct_type1 { ... }: struct
struct_type2 { ... struct_type1 struct_member;
... };
UNIONS
A union is a structure that really only holds one element, but
that one element can have more than one type and more than
one name. In this section we introduce unions, but their
practical use is hard to illustrate without arrays (a future topic).
Unions are rarely used, but when they are needed it is nice to
have them. The basic structure of a union looks very much like
a structure:
union TypeName { // list of union members };
As in the structure the list of members can be of various types.
The difference between a structure and a union is: the space
allocated to a union is only as big as its biggest member. In the
following we have a union with 2 members: a short (2 bytes)
and an int (4 bytes), so the space allocated to the union is 4
bytes:
union example_t { int member_name1; short
member_name2; }; example_t union1;
union1.member_name1 = 146834; // sets all 4 bytes
union1.member_name2 = 146834; // sets only the
// lower 2 bytes
7. BASIC DEFINITIONS
An array is a finite set of elements stored in memory in which
the elements are all of the same type. Hence, arrays are distinct
from structures in that structures can contain elements of any
type. The number of elements in an array is called its size.
Arrays can consist of elements of any single type, but we will
begin with character arrays.
Recall that characters typically require one byte of storage per
character, so an array of 8 characters will require 8 bytes; we
declare an array by naming the type, naming the array, and
indicating its size inside the array symbol: [ ].
char myArray [8];
We can also initialize an array in its declaration.
char myArray [] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
Notice that in this case we did not have to indicate the size in
between the brackets, as the size is indicated by the set of
initial values (compilers can count).
Now that we have an array of 8 characters, we access an
individual element by using the index of the individual element.
The index of the first element in the array above ('A') is 0; the
index of the last element ('H') is 7. In general (and this is true
of all languages derived from C) an array with n elements has
indices ranging from 0 to n-1. If you try to reference the nth (or
higher) element you will be referencing a non-array element and
this is an error: out of range; you will commit the same error if
you use a negative index.
Arrays are most often processed by for loops, but usually any
loop will do as well. Here is a code snippet that initializes an
array, sums up the elements of that array, and computes the
average. Note especially that the limit on the for loop uses <,
NOT <=.
const int ARRAYSIZE = 10; ... int numArray
[ARRAYSIZE] = {2, 6, 3, 8, 19, -1, 0, 22, -6, 10}; int i, sum =
0; double average; ... for (i = 0; i < ARRAYSIZE; i++)
{ sum += numArray[i]; } average = (double)sum
/ (double)ARRAYSIZE;
8. LAYOUT OF ARRAYS IN MEMORY
We will think of arrays as being laid out in memory
sequentially. Our character array above has 8 elements, so 8
bytes are allocated to hold it, and each 1-byte element of the
array has its own unique address in memory; memory is byte-
addressable. Our int array above has 10 elements, and each
element is 4 bytes (for a total of 40 bytes); the lowest address
of each 4-byte integer accesses each of the 4-byte elements.
You can think of arrays in memory as laid out horizontally
(with addresses increasing from left to right), OR you can think
of arrays in memory as laid out vertically (with addresses
increasing from bottom to top or top to bottom). However,
because we usually reserve the vertical way of thinking for the
stack, here we will think of arrays in memory as laid out
horizontally.
PARITALLY FILLED ARRAYS AND ARRAY OVERFLOW
If you use an 8-byte character array such as that declared above,
there is no requirement that you have to fill every byte. An 8-
byte character array may only hold 4 bytes, or any number less
than or equal to 8 bytes. However, you cannot use an 8-byte
array to hold more than 8 characters, say 10, and if you try to
do so you may get a runtime error; sometimes the compiler will
pick this up. These runtime errors are devilishly hard to find,
and finding them often requiresthat you use a debugger.
Note especially that if the compiler does not pick up any out-of-
range references, then the runtime environment will not flag any
either, unless the program attempts to reference memory outside
of the program's allocated memory space. The implication here
is that out-of-range memory reads/writes can wreak havoc for
an executing program. Programmers need to be wary of this
possibility.
PASSING ARRAYS TO FUNCTIONS
Arrays and elements of arrays can be passed to functions. If you
9. want to pass the 4th element of a 10-element integer array, you
can do so as follows; using numArray declared above:
int myFunction (int); // prototype ... int index = 3; int
ret; ... ret = myFunction (numArray[index]); ...
You can pass in an array element and make it possible for the
called function change the element by passing it by reference:
int myFunction (int&); // prototype ... int index = 3; int
ret; ... ret = myFunction (numArray[index]); ...
The entire array can also be passed into a function, but in this
case you must also pass in the size of the array:
int findLeast (int [], int); // prototype ... int ret; ret
= findLeast (numArray, ARRAYSIZE); ...
However, if you pass the entire array into a function, the
compiler will not put the entire array on the stack (who knows
how many elements that could be). Instead, the compiler will
place a pointer to the array on the stack, that is, the address of
the first element of the array. In effect, this amounts to passing
the entire array by reference and allows the called function to
modify the array. If you do not want to give the called function
the ability to modify the array, you must declare the array
passed as const.
int findLeast (const int [], int); // prototype
ARRAYS OF OTHER TYPES
So far, we have used examples of character and integer arrays,
but any type, whether built-in or user-defined, can be used in
arrays. Always remember that arrays can consist only of objects
of the same type. So, arrays can consist of:
· Characters
· Integers: short, int, long
· Decimals: float, double, long double
· Strings
· Structures and unions: struct and union
· Classes
· Even arrays themselves (arrays of arrays are multi-
dimensional arrays; see below)
10. STRINGS AND CHARACTER ARRAYS
Recall that C++ makes a distinction between character arrays
and strings: string is actually a class. When we open a file we
can pass as its name a literalstring, such as: "myTextFile.txt".
Or, we can pass a string variable to the open call. But, in order
to do so, we must convert the string to a character array.
string myFile = "myTextFile.txt" ... ifstream inFile
(myFile.c_str(), ios::in); ...
The c_str() string function converts the string myFile into a
traditional C-language character array. In C, character arrays
that represent strings are always terminated by a null byte: '0'
(an ASCII character equal to 0).
Most C++ programmers prefer to use the string class instead of
the traditional C-language character array, as the string
operators, such as ==, !=, and +, are extremely convenient.
TWO-DIMENSIONAL ARRAYS
We mentioned earlier that you can have arrays of arrays. Most
often these are 2-dimensional arrays, which we can think of laid
out in rows and columns, just like a spreadsheet.
We access these arrays by supplying a row index and a column
index. The following is an example of initializing a 2-
dimensional array that has 300 rows and 10 columns: 3000
elements altogether. Note that the row number is always first, as
it is the primary index. The column number is secondary. Row
indices can range from 0 to 299; column indices can range from
0 to 9.
int array2D [300][10]; ... for (int i = 0; i < 300; i++)
for (int j = 0 j < 10; j++) array2D[i][j] = 0;
...
When passing two-dimensional arrays to functions
you should also pass both dimensions:
void myFunction (int [ ], int, int); // prototype ... int
myArray [100] [10]; ... myFunction (myArray, 100, 10);
Two-dimensional arrays will be covered further in the module
11. on matrices and linear systems.
MULTIDIMENSIONAL ARRAYS
We can, of course also have 3-dimensional, 4- dimensional, ...
arrays, but these are harder to picture, A 3-dimensional array
will have 3 indices. You can think of a 3-dimensional array as a
2-dimensional array in which each cell in that array has many
elements. A visual analogy would be a solid in 3-dimensional
space (mathematical 3-space does NOT have a finite number of
points, whereas arrays in C++ are necessarily finite).
int array3D [2][3][4]; // prototype ... array3D[0][2][1]
= 0; ...
Of course, 4-dimensional arrays are even harder to imagine, but
an analogy can be made to the 4-dimensional space-time.
REVIEW EXERCISES
1. *The following is a diagram of the memory used by an array
of 8 8-bit (1-byte) characters. Do a similar diagram for an array
of 8 32-bit integers and 8 64-bit doubles.
Byte (Character)
1
2
3
4
5
6
7
8
Index
0
1
2
3
4
5
6
12. 7
Address
n
n+1
n+2
n+3
n+4
n+5
n+6
n+7
2. *What is wrong with the following code snippet?
int myArray [8]; for (int i = 0; i <= 8; i++)
myArray [i] = 0;
3. *What is the dimension of the following array, and what is its
size? Assuming a double is an 8-byte quantity, how many bytes
of memory will the array occupy?
double dArray [2][5][3][4];
4. **C++ has no built-in facility for displaying numbers in
binary. The number 45 base 10 is 00101101 base 2. How can
you display a int (4 bytes max) in binary using cout? Group the
output into 8 blocks of 4 binary digits; that is, 45 should appear
as:
0000 0000 0000 0000 0000 0000 0010 1101
5. **Write a program that initializes an array with the first 30
numbers in the Fibonacci series and then prints out the contents
of the array. The Fibonacci series is generated by the following
rules: both the first and second numbers are 1, every number
after that is the sum of the previous two numbers in the series.
6. ***Bubble Sort. A bubble sort is a sorting of a list of
numbers. The sort can be in either ascending or descending
order. The bubble sort algorithm makes multiple passes through
the list and on each pass examines each adjacent pair of
members on the list and interchanges then if they are not in the
right order. The bubble sort terminates when a pass is made that
requires no interchanges. Beginning with the list: 9, 8, 7, 6, 5,
13. 4, 3, 2, 1, sort this list in ascending order using a bubble sort
that you implement. Test your program using random orders of
the integers 1 to 9.