element
key value
sequential search
parallel arrays
numeric arrays
There is little need to explain arrays in great detail here because you already know all about them! You've been working with character arrays since Lesson 3. This unit simply explains more about how Visual C++ stores arrays and introduces arrays of
data types that are different from the char data types you've been working with.
In addition to character arrays, you can create arrays of any data type. You can define an integer array, a floating-point array, and a double floating-point array. Any time you need lists of values, it's a lot easier to define the list as an array
instead of as differently named variables. You can step through all the elements of an array with a for loop, and you'll often need to process and print elements throughout an array.
Review the terminology of arrays and explain how arrays reside in the computer's memory.
Figure 15.1 shows a character array named cArray with five values. Each individual value in the array is an element. The subscripts range from 0 to 4 for a five-character array. No matter what kind of array you define, each value in the array is
called an element, and the number that references each element is also a subscript.
Figure 15.1. A five-element character array holding a string.
As you know, character arrays hold strings. In Visual C++, arrays can be taught much earlier than in other programming languages (or at least character arrays can be). When a character array holds a string value, treating the array as a single but
aggregate string value with the individual elements holding individual characters makes a lot of sense. (Other programming languages store strings in string variables.)
You might not see the need for numeric arrays at this time. After all, if you defined an array of 25 integers, you would never refer to the entire integer array as a single entity. The concept of doing something with a single group of 25 numbers doesn't
make as much sense as treating a character array holding a string as a single group.
In reality, you work with lists of numbers all the time. When your program must process several numeric values, array storage is perfect for holding and stepping through those values. Although you don't refer to the array aggregately in the same way
that you do a character array holding a string, numeric arrays are extremely important in computing.
To help with your understanding of numeric arrays, let's review the basics of character arrays. The array in Figure 15.1 might be initialized like this:
char cArray[5] = "Star";
Visual C++'s syntax for initializing strings in character arrays, as shown here, doesn't apply to numeric arrays. There's no need to worry about a null zero at the end of numeric arrays because only strings need null zero values at their termination. If
you define and initialize a numeric array at the same time, you must initialize the array's individual elements one at a time. Use braces to initialize individual elements in the array.
Here's how you would initialize cArray one element at a time:
char cArray[5] = {'S', 't', 'a', 'r', '\0'};
This definition is identical to the previous one, except that it's a little more tedious for you to type. Nevertheless, this character-by-character initialization will prepare you for the syntax required for all numeric array initialization. Visual C++
automatically adds the terminator zero to the end of the string when you assign an initial string value.
When you define and initialize an array at the same time, you don't need the initial subscript. The following definition is identical to the preceding one:
char cArray[] = {'S', 't', 'a', 'r', '\0'}; // Visual C++ counts the // elements for you
When you (or Visual C++) reserve five array elements, don't try to store six or more elements in the array! If you do, you will overwrite memory that might be holding other kinds of variables. If you need to define a large array, you can always define
the array with extra elements. The following array definition holds the four-character string Star with a terminator, but it also has extra reserved room in case you need to store a longer string in the array later:
char cArray[35] = {'S', 't', 'a', 'r', '\0'};
Character arrays might hold individual characters and not strings. If that's the case, don't worry about the terminator. It exists only to terminate strings.
String functions such as strlen() and strcpy() rely on the terminator. If you define an array to hold a list of individual characters, such as a class's letter grades, you'll never treat the array as if it holds a string; therefore, you don't have to make sure the terminator is at the end of the array.
Unlike most programming languages, Visual C++ doesn't complain if you store a value in an undefined array element. For example, the statement
cArray[3200] = 'X'; // Oops!puts an X approximately 3,200 memory locations after the start of the 35-character array named cArray. Who knows what the X will overwrite? Whatever happens, you'll realize something has gone awry when your computer freezes or reboots because the X overwrote an important area of memory or changed a critical internal program value! Moving to numeric arrays is now easy. If you want to define a five-element integer array named nums, you can do so like this:
int nums[5]; // Define an integer arrayYou also can initialize the array at the same time you define it:
int nums[5] = {2, 4, 6, 8, 10}; // Define and initialize the arrayFigure 15.2 shows how the integer array looks in memory. Notice that an integer array and a character array are the same except for these two things:
- Numeric arrays contain numeric values and not character values.
- Numeric arrays don't use a terminating zero.
Figure 15.2. A five-element integer array holding five integers.
As with character arrays, you don't need to specify the initial subscript if you define the array with its maximum number of values up front. This array definition matches the preceding one:
int nums[] = {2, 4, 6, 8, 10}; // Defines and initializes the arrayIf you define an array without assigning initial values at the time of definition, be sure to use an initial subscript. The following array definition is incorrect. Visual C++ reserves no storage for the array! int array[]; // There must be a subscript // or initial values
Here is another integer array definition:
int values[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
Despite the fact that a 0 is in values's last element, that 0 is considered an integer zero, because there is no string in the array. In other words, it is considered a valid part of the array data.
If you don't initialize an array when you define it, you must fill the array one value at a time. For example, this is not possible in the body of the program:
values[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
And neither is this:
values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
In the body of the program, you must assign each value to each array element one at a time. Later in this unit, you will learn some techniques that most programs use to initialize arrays.
The following two array definitions reserve two numeric arrays. The first array is a double array and the second is a long int array:
double factors[25]; // Defines an array of 25 doubles
long int weights[100]; // Defines an array of 100 long integers
At the time you define these arrays, you could initialize them with a few values, like this:
double factors[25] = {4244.56, 78409.21, 2930.55433, 569401.34};
long int weights[100] = {43567, 70935, 32945, 102, 49059};
If you define an array of a specific size but initialize fewer elements as shown here, Visual C++ zeros out every remaining element in the array. In other words, the first five elements of weights contain the values inside the braces (from 43567 to
49059), and the remaining elements hold zero.
If you don't initialize any elements, don't rely on Visual C++ to initialize them for you! Visual C++ doesn't automatically initialize vals to zeros given this definition:
int vals[100]; // Defines but doesn't initialize vals
If you want to initialize an array to all zeros, initialize it with a single zero:
int vals[100] = {0}; // The entire array now holds 0
Array Space
Each element in an array requires a different subscript. All arrays, no matter what data type they are, start at subscript 0. The next array element is subscript 1, and so on.
Each element in an array of double values will consume more memory than each element in an array of character values. A five-element character array will consume much less space than a five-element double floating-point array. You reference both, however, with the subscripts 0 through 4.
If you want to know how much memory an array consumes, use the sizeof operator on the array name. The following statement stores the total amount of memory taken by the array named factor to mem:
mem = sizeof(factor);
If factor were a character array, mem would hold a much smaller number than if factor were a floating-point array because each element in the character array would take much less memory. sizeof returns the total amount of memory an array is defined as. sizeof has nothing to do with how many values you've actually initialized with data.
Many times, you can't initialize an array when you first define it. The user's input or data from a disk file or calculations might fill the array with values in the body of the program. The assignment operator stores values in arrays. Given the array
definition
float ara[10]; // Define an array of 10 floating-point values
you can later store a number in the first element with an assignment like this:
ara[0] = 293.05;
You can also get an element with cin to let the user assign a value to the element:
cout << "What is the next value? "; cin >> ara[4];
The following for loop stores the values 0 through 450 in an integer array that's assumed to be defined to hold at least 451 elements (one for element 0):
for (s = 0; s <= 450; s++) { iArray[s] = s; }
Listing 15.1 contains a simple program that defines an array of seven floating-point values. The user initializes each value with a daily temperature in Fahrenheit. The program then applies a calculation to each element to convert
those Fahrenheit values to Celsius. When all values have been converted, the program prints the Celsius readings.
You can define an array of any data type and step through each element.
1:// Filename: ARRTEMP.CPP 2:// Fills an array with seven temperature readings, converts 3:// those readings to Celsius, and prints the results 4:#include <iostream.h> 5: 6: 7:void FillTemps(float temps[7]); 8:void CalcCelsius(float temps[7]); 9:void PrintCelsius(float temps[7]); 10: 11:void main() 12:{ 13: 14: float temps[7]; // The user will initialize the array 15: 16: FillTemps(temps); // Get the seven values from the user 17: CalcCelsius(temps); // Convert the values to Celsius 18: PrintCelsius(temps); // Print the Celsius readings 19: 20: return; 21:} 22://*********************************************************** 23:void FillTemps(float temps[7]) 24:{ 25: int ctr; 26: cout << "** Temperature Conversion **" << endl; 27: cout << "----------------------------" << endl << endl; 28: for (ctr = 0; ctr < 7; ctr++) 29: { 30: cout << "What is the Fahrenheit reading for day #" 31: << (ctr+1) << "? "; 32: cin >> temps[ctr]; 33: } 34: return; 35:} 36://*********************************************************** 37:void CalcCelsius(float temps[7]) 38:{ 39: // Change each temperature to Celsius 40: int ctr; 41: for (ctr = 0; ctr < 7; ctr++) 42: { 43: // The Fs after the numbers are floats not Fahrenheits 44: temps[ctr] = (temps[ctr] - 32.0F) * (5.0F / 9.0F); 45: } 46: return; 47:} 48://*********************************************************** 49:void PrintCelsius(float temps[7]) 50:{ 51: // Print the Celsius readings 52: int ctr; 53: cout.precision(1); 54: cout.setf(ios::showpoint); 55: cout.setf(ios::fixed); 56: cout << endl << "Here are the equivalent Celsius readings:" 57: << endl << endl; 58: for (ctr = 0; ctr < 7; ctr++) 59: { 60: cout << "Day #" << (ctr + 1) << ": " 61: << temps[ctr] << endl; 62: } 63: cout << endl << "Stay warm!" << endl; 64: return; 65:}
Output
** Temperature Conversion ** -------------- What is the Fahrenheit reading for day #1? 65.7 What is the Fahrenheit reading for day #2? 45.4 What is the Fahrenheit reading for day #3? 32.1 What is the Fahrenheit reading for day #4? 28.3 What is the Fahrenheit reading for day #5? 24.5 What is the Fahrenheit reading for day #6? 67.8 What is the Fahrenheit reading for day #7? 76.2 Here are the equivalent Celsius readings: Day #1: 18.7 Day #2: 7.4 Day #3: 0.1 Day #4: -2.1 Day #5: -4.2 Day #6: 19.9 Day #7: 24.6 Stay warm!
Analysis
One of the first things to note about this program is that all the temperature readings are asked for in advance, all at once, before any calculation is performed. You could never program this way without arrays. Arrays give you the ability to capture
all your data in a loop and then process that data all at once. Without arrays, you would have to use separate variable names for each temperature and ask for the seven readings using seven different sets of the same I/O code.
main() is a controlling function that dictates what the rest of the program is to do. If you hadn't read the description of the program beforehand, you could still figure out the entire program's job by studying main(). One of the many advantages of
structuring your program into smaller modules is the readability of the main() function.
The FillTemps() function in lines 23 through 35 gathers all the temperature readings in one for loop. As you begin to write longer and more powerful programs (this is one of the longest programs in this book so far), you'll see that gathering data up
front, before processing, often helps eliminate messy get-a-value, calculate-a-value code. By storing data in an array first, you can better structure your programs. When you begin to write really big programs (several pages long), everything you can do to
help streamline the code will save you time during both program development and debugging.
Do you think that this long program is difficult to understand? On the contrary; you probably think it's rather simple. However, this program is one of the most complex in this book so far! You're now at the point where more advanced Visual C++ code seems easy. The structuring of programs into smaller functions helps you see "the big picture" better than before.
This program always passes the array by address. Therefore, the functions can all change the array. When they do, main()'s version also is changed because the functions operate on the same array as main(), not on a copy as would be the case with data
passed by value.
You might think that a global array of temperatures would be better than passing the same local array through all the functions. It's true that a global variable makes sense if every function in the programor at least most of the functionsneeds access to the variable. However, my desire to shy away from global variables as much as possible dictated the use of one local array instead. The extra practice at passing data between functions is good for you.
definition
A key is a value you're looking for in an array or a file.
One of the most important features of array processing is searching through an array for specific values. You can tell your computer to find a customer balance, receivable amount, or payroll figure, and it responds in a snapeven if the program has to search through hundreds of values.
Searching an array for a particular value is simple. Array searching requires no new commands, just an application of the commands you already know. Once an array is filled, finding a value requires starting at the beginning of the array and comparing
the next array element with a key value.
All kinds of searching techniques for computers have been developed. The technique discussed in this unitthe sequential search techniqueis the most popular and one of the easiest, but it's also one of the slowest. Unless your programs often search through huge arrays of data, however, the sequential search gives you more than enough power to find a match in a short time.
For example, suppose you need to see whether a customer properly filled out his or her customer number on a mailing that you recently sent. You can scan the customer ID list, an array of values, looking for the customer's ID (the key).
When you search an array for a key value, one of three things will happen:
Your data will dictate whether you'll allow duplicate keys. In other words, you don't want two customers with the same customer ID number. Therefore, every time you add a customer to your files, through array processing you'll want to make sure that the
customer isn't already on the list. Other times, you'll want to allow two or more equal values in an array. A list of student test scores might produce two or more students who happened to make the same grade on a test.
Searching an array makes the assumption that the array already has data. There are three primary ways that you fill arrays with values:
This book has spent a lot of time assigning values with the assignment operator, but most real-world data comes from the user or the disk. You can't know when you write a program exactly what values that program will eventually process, so you can
rarely assign data in assignment statements. You can't predict how much a customer will buy, so you have to wait for the clerk at the computer to enter the amount.
The rest of the programs in this unit assign array values directly or through user input because you have yet to learn about file I/O. Lesson 11 explains how to use disk files for storage. When you start a program that manages disk file data, probably
one of the first things the program will do is read the disk values into an array. Therefore, a prerequisite for learning about disk files is learning how to work with arrays.
In Lesson 12, you'll learn about random file access, which is a fancy term that basically means treating a disk file as if it were one large array. Instead of taking up valuable memory, you can keep the data on the disk and apply this lesson's array techniques to the file itself. As you can see, array processing is vital for your future programming.
Here is a simple loop that searches an array with MAX values, looking for the user's match:
cout << "What is your customer code? "; cin >> userKey; match = 0; // No match is made yet for (c = 0; c < MAX; c++) { if (customers[c] == userKey) { match = 1; break; } // 1 is true } if (match) { cout << "You are already in the list"; } else { cout << "You are not in the list"; }
In this code fragment, userKey is the key value that you're looking for. Searching almost always involves a variable that holds a true or false result. Either the search key was found in the array (true) or it was not found (false). In this code, the
match variable begins as false before the search begins. If there is a match of the customer's number to an array element, match is set to 1 (true) and the break terminates the for loop because there is no reason to look through the remaining elements.
If you were applying this sequential search against an array that holds duplicate keys, you wouldn't break out of the loop but would continue searching. Such searches often add all of the matches found (using a counter variable). A weather center might look through an array of last year's temperature readings for all values below freezing, counting the number of values that meet that criteria. Because two or more days could be below freezing, you wouldn't stop the search after finding just one.
Are You "Just a Number"?
Do you get tired of feeling as if utilities and credit card companies think of you as just a number instead of a real person? Blame the computer!
Many names, such as McNeal and St. John, have far too many variations to be used as a key when searching with a program. McNeal might be entered as McNeal, Mc Neal, Mcneal, or Mc.Neal, depending on the person doing the data entry. An integer or character string is a much more accurate way of assigning specific values to people. The customer code of 734AB has little ambiguity other than the possibility of the user entering lowercase letters, which your program can convert itself or by calling a Visual C++ library routine (see Lesson 12).
When businesses computerize their records, they must assign their customers, vendors, and products unique IDs (key-searching values) to speed up the searching. Ideally, these computerized filing systems ultimately save you lots of money over file-cabinet organization. Can you imagine how long it would take a credit card company to go through miles of filing cabinets looking for your balance when you wanted to charge something? Computers speed up important business processing, and that saves youthe customer and consumerlots of dough.
Before looking at code that searches an array, you've got one more concept to understand. Look at Figure 15.3, a spreadsheet-like representation of a customer file. The first column is a list of customer IDs, the second column contains customer balance
amounts, and the third column is the customers' last purchase amount.
Figure 15.3. A three-array representation of customer data.
definition
Parallel arrays are several arrays with the same number of elements that work in tandem to produce data.
Rarely will you have to search a single array to look for a value. More likely, you'll have to search several parallel arrays. Figure 15.3 contains three parallel arrays. The first array is the customer ID, the second array is the balances, and the
third array is the last purchase amount. Each array would be parallel, meaning that the first element ID in the first array would have the balance shown in the first element of the second array. That same customer's last purchase amount is listed in the
first element of the third array. For example, customer number 4967 has a balance of $3,249.55 and last made a purchase of $573.68.
Here's how parallel arrays work: The data from the arrays would probably come from a disk file, as discussed earlier. After the arrays are filled with their values, a customer comes into your store to buy something. The first thing you do is have a
program look through the customer ID array. When the customer's ID is found, use that element number to look in the balance array to see whether the customer's balance is too high to buy something. If so, the customer must pay some of the balance. You
could use this same procedure to see whether the customer's last purchase was large, in which case you might want to give a special discount today.
The arrays that you've learned about in this book are called single-dimensional arrays. Visual C++ supports multidimensional arrays, which hold data in a tabular format, but multidimensional arrays aren't as commonly used in Visual C++ as they
are in other programming languages. You can rarely replace parallel arrays with multidimensional arrays, even though multidimensional arrays have a tabular format similar to that of Figure 15.3. Multidimensional array elements must all be the same data
type, which limits their use as a replacement for parallel arrays.
Also, Visual C++ doesn't really have built-in support for multidimensional arrays as other languages do. To create a multidimensional array, you must create an array of arrays, which can greatly increase program complexity without adding a lot of
benefit.
If you're lost, that's fine because this book doesn't discuss multidimensional arrays any further. Again, their use is limited, and taking the time to discuss them in detail here would take away space from more important material. Later, you will see
another way of holding different types of values in a single-dimensional array.
Listing 15.2 contains an inventory program. Initialization statements fill the parallel arrays with product ID numbers, product amounts, product prices, and product reorder levels. The user enters a product ID, and the program then
displays the product's inventory figures.
Searching parallel arrays requires that you look for a match on a key value. If a match is found, the matching elements in the other arrays provide additional data.
1:// Filename: INVSRCH.CPP 2:// Demonstrates the sequential parallel array searching 3:// technique. An inventory ID number is asked for. If 4:// the ID is found in the arrays, that product's 5:// inventory data is displayed for the user. 6:#include <iostream.h> 7:#include <iomanip.h> 8: 9:int GetKey(); 10:int SearchInv(int keyVal, int prodID[]); 11:void PrintData(int foundSub, int prodID[], int numItems[], 12: float price[], int reorder[]); 13: 14:const int INV = 10; // Products in this sample array 15: 16:void main() 17:{ 18: 19: // First, initialize a few sample elements 20: // Product IDs 21: int prodID[INV] = {32, 45, 76, 10, 94, 22: 52, 27, 29, 87, 60}; 23: // Number of each product currently in the inventory 24: int numItems[INV] = {6, 2, 1, 0, 8, 25: 2, 4, 7, 9, 3}; 26: // Price of each product 27: float price[INV] = {5.43, 6.78, 8.64, 3.32, 1.92, 28: 7.03, 9.87, 7.65, 4.63, 2.38}; 29: // Reorder levels 30: int reorder[INV] = {5, 4, 1, 3, 5, 31: 6, 2, 1, 1, 4}; 32: int keyVal, foundSub; 33: 34: // The program's primary logic begins here 35: keyVal = GetKey(); // Ask the user for an ID 36: foundSub = SearchInv(keyVal, prodID); // Search the inventory 37: if (foundSub == -99) 38: { 39: cout << "That product is not in the inventory."; 40: return; 41: } 42: // Here only if the item was found 43: PrintData(foundSub, prodID, numItems, price, reorder); 44: return; 45:} 46://********************************************************** 47:int GetKey() 48:{ 49: // Ask the user for an ID 50: int keyVal; 51: cout << "** Inventory Search Program **" << endl; 52: cout << endl << endl << "Enter a product ID: "; 53: cin >> keyVal; 54: return (keyVal); 55:} 56://********************************************************** 57:int SearchInv(int keyVal, int prodID[]) 58:{ 59: // Search the ID array and return the subscript of 60: // the found product, or return -99 if not found 61: int foundSub = -99; 62: int c; 63: for (c = 0; c < INV; c++) 64: { 65: if (keyVal == prodID[c]) 66: { 67: foundSub = c; 68: break; 69: } 70: } 71: // The -99 will still be in foundSub 72: // if the search failed 73: return (foundSub); 74:} 75://********************************************************** 76:void PrintData(int foundSub, int prodID[], int numItems[], 77: float price[], int reorder[]) 78:{ 79: // Print the data from the matching parallel arrays 80: cout << setprecision(2); 81: cout.setf(ios::showpoint); 82: cout.setf(ios::fixed); 83: cout << endl << "Product ID: " << prodID[foundSub] 84: << "\tNumber in stock: " 85: << numItems[foundSub] << endl; 86: cout << "Price: $" << price[foundSub] << "\tReorder: " 87: << reorder[foundSub] << endl; 88: return; 89:}
Output
Two runs of output are shown.
** Inventory Search Program ** Enter a product ID: 13 That product is not in the inventory. ** Inventory Search Program ** Enter a product ID: 94 Product ID: 94 Number in stock: 8 Price: $1.92 Reorder: 5
Analysis
main(), from lines 16 through 31, consists primarily of array initialization. As mentioned earlier in this unit, your real-world programs will probably initialize arrays from disk files or user input. Inventory programs are almost always designed to
initialize from a disk file because the user shouldn't be expected to enter the inventory data each time he or she runs the program.
After the arrays are initialized, main() controls the rest of the program. The user enters a product ID in the GetKey() function. SearchInv() searches the product ID array, looking for a match. If the product ID isn't found, SearchInv() stores 99
for the subscript, which indicates that the ID isn't in the list and causes an error to appear in main(). If a match is found, the subscript for that product's parallel arrays is returned to main(), where it's passed on to the PrintData() function, along
with the four parallel arrays.
PrintData() then uses the subscript of the found product to print the inventory information from the arrays. When main() regains control, the program quits and returns to the Visual C++ QuickWin window.
The logic in this program is fairly complicated, yet you should have little trouble understanding what's going on. No new commands or library functions are needed. Arrays make stepping through and searching a list of values simple. When you add file I/O
to a program such as this, reading that file's data directly into the arrays, you have a full-featured inventory searching program.
When you complete the questions at the end of this unit, try your hand at the last two Extra Credit exercises. They require that you write a program similar to this inventory program and add some updating and reporting functions. You'll develop a
full-featured inventory-balance control system based on what you already know.
Fewer than seven lessons ago, you were figuring out the difference between an integer and a floating-point value. Now you're writing high-level business programs!
double values[16];
int ages[] = {23, 54, 32, 39, 40, 69, 74, 57, 12};
float weights[];
long double scientific[25];
int values[] = {5, 2, 1, 9, 8, 0};
int vals[8]; double vals[8];
int vals[8]; double vals[8];
int ara[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (c = 8; c >= 0; c-) { cout << ara[c] << endl; }
cin >> keyVal; for (i = 0; i < NUM; i++) { if (ara[i] == keyVal) { found = 1; } // Found a match else { found = 0; } // Not at this element }
Here are your choices: 1. Display a product balance information 2. Update a product's stock with a receipt (+) or issue (-) 3. Print a list of all products which need reordering 4. Exit the program What do you want to do?