C++ Exercise 6
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.
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 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
REVIEW EXERCISES
1. *Design a structure to represent a student's master record (master data is data that rarely changes). What members would you include and what are each of the members' types?
2. *Design a structure to represent a student's transcript; you do not have to include all the data is the student's master record. What members would you include and what are each of the members' types?
3. **When might a union be useful?
ARRAYS
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
In this module we will be almost exclusively concerned with the built-in complex type array. Arrays make it possible to deal with sets of data (such as a set of student records, each of which has the same structure or type).
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;
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 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)
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 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 |
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, 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.