CHAPTER 10

Arrays and cStrings

Chapter Objectives

By the end of the chapter, readers will be able to:

images  Explain in a language-independent fashion the concepts of single and multidimensional arrays.

images  Illustrate the layout of arrays in memory.

images  Declare and initialize both single and multidimensional arrays.

images  Apply both single and multidimensional arrays to programming solutions.

images  Demonstrate the characteristics and application of cStrings.

images  Use a variety of predefined cString functions.

images  Explain the concepts of parallel arrays.

Introduction

As the text has progressed, our programs have become more and more complex. However, the amount of data required for these programs has remained relatively small. The reason for not requiring large amounts of data lies in the difficulty associated with storing that much information. Multiple variables could have been used, but that would have been very challenging and time consuming to manage. For example, using the variables assign1, assign2, assign3,…, assign100 would be awkward. Also, you have probably noticed that we have not asked you to enter strings from the keyboard. All of these issues can be addressed and programs made more efficient by using arrays. This chapter is dedicated to the language-independent theory behind arrays as well as their C++ implementation.

10.1 What Are Arrays?

Arrays allow us to associate multiple pieces of data with one variable name. Let's examine the definition of an array in greater detail.

There are three key points in this definition. The first is the phrase “two or more…memory cells.” This alludes to the fact that we never want to declare an array that holds a single piece of data, often called an element. If you need to have a single-element array, use a regular variable instead.

The second point relates to the phrase “contiguous memory cells.” This means that each element of the array is physically adjacent to each other in memory.

The third key point in the array definition is “homogenous data type.” This means that an array can only contain data of a single type. Although this data type can be any of the types we have discussed, you can't mix data types within the same array.

The definition of arrays just discussed can be applied to any programming language. One key point we have not examined is how arrays are implemented specifically in C++. The variable name associated with an array refers to an address, not the actual data.

Even more importantly, the address is a constant address, meaning that an array can never be placed on the left side of an assignment operator. Therefore, an array is not an appropriate l-value.

Now you know the fundamentals associated with arrays, but you still may not understand when arrays would be useful in your algorithm development. To help illustrate the usefulness of arrays, assume we need to store the test scores for 25 students. We could declare an array that contains 25 elements. Each element, or cell, would represent a score for one student. Before continuing our discussions, the next section provides a quick summary of the pros and cons associated with arrays.

10.1.1 Advantages and Disadvantages of Arrays

Arrays offer a number of advantages, including providing the opportunity to declare a number of related elements, all of the same type, quickly. In addition, arrays can be used with any of the data types we have already discussed. Likewise, arrays are useful tools when needing to sort or search for information, as you will see in Chapter 11. Finally, when using the various looping structures presented in previous chapters, arrays provide a fast and easy method for accessing a potentially large amount of data.

On the other hand, arrays can create additional costs or potential disadvantages as well. For example, if we declare an array to hold 100 elements, but end up needing and using only 10 elements, we have unnecessarily wasted our computer's resources. Likewise, if while accessing our arrays we go outside the block of memory allocated, we have just created one of the more difficult programming errors to find and correct.

10.2 Declaring Arrays

The syntax for an array declaration is as follows:

<data-type> <array-name> [ <number-elements> ];

The data type and the naming rules and conventions remain the same as they were for ordinary variables. The <number-elements> must be an integer constant or a literal that specifies the total number of elements. It is syntactically incorrect to use a variable to represent the number of elements in the array declaration. Example 10.2.1 contains several array declarations.

images

Example 10.2.1 shows a number of different arrays being declared. Example 1 declares a character array called first_name with 20 elements. In the second example, last_name is declared with the number of elements established by the use of a constant called LEN. Example 3 creates an array made up of five individual integer elements. The final example creates two different arrays of integers, the first called lab_scores and the second assignment_scores, both of which contain four elements.

Each element in an array can hold exactly one value of the specified type, even though the value could physically occupy more than one byte. This means that if we have an array of 10 floats, the array will occupy 40 bytes of memory. The memory required for the array is calculated by multiplying the number of elements by the size of the data type.

Since arrays are a fixed size, care needs to be taken when the array is declared to make sure the size is appropriate for your data. For example, assume that at the beginning of the term a class might have 30 students, but toward the end of the term only about 20 students may remain. You would still need to allocate room for 30 students because that is your maximum limit, or size. Sometimes determining the maximum size is a difficult decision. You really need to know a lot about your data and have a thorough understanding of the problem you are trying to solve.

If you make an error in judging the size of your array, make sure you have made it a bit larger than required by your data rather than making it too small. It is better to waste a little memory than take the chance of going out of bounds of the array.

STYLE NOTE Use constants to specify the number of elements in an array. Constants not only make your code more readable but also make your code easier to modify. If the size of the array needs to change, you need only change the value of the constant and recompile. To gain the biggest advantage, use the constant whenever referencing the array bounds.

Section 10.2 Exercises

Please respond to each of the following questions.

1.  The _________________ of an array actually refers to an address.

a.  size

b.  dimensions

c.  name

d.  data type

2.  Based on the following line of code, how many different elements would the array test hold? int test[5];

a.  4

b.  5

c.  6

d.  unknown

3.  How many values can each element in an array hold?

a.  0

b.  1

c.  2

d.  unknown

4.  Indicate whether each of the following statements is true or false.

a.  An array is a group of three or more contiguous memory locations composed of the same data type.

b.  While syntactically possible, we would not declare an array with only one element.

c.  When possible, we will use literals to indicate the size of an array.

5.  It is best to use a _____________________ to indicate the number of elements in an array and when referencing the array bounds.

a.  variable

b.  literal

c.  constant

d.  type

10.3 Using Arrays

To access a specific element in an array, you must use brackets ([ ]), also called the subscript operator. The subscript operator actually specifies the offset from the beginning address. Therefore, to access the first element, you need to specify an offset of 0; to access the second element, an offset of 1, and so on. This process is illustrated in Figure 10.3.1.

Figure 10.3.1 simulates how the array test_scores might appear in memory. While we have indeed made up the first or beginning address location, 1000, a number of important points are worth noting. As previously mentioned, the first element of the array has an offset of 0; the second element, an offset of 1. Second, notice that each individual element of the array is the same size, or four bytes, representing the size of an integer. Finally, observe that the individual elements are contiguous, or physically located one after another.

The number within the brackets in the declaration of an array specifies the total number of elements. The number in the subscript operator specifies the offset from the beginning address. Example 10.3.1 shows the various uses of the brackets.

images

images

Figure 10.3.1 Memory layout of an array

In Example 10.3.1, the number placed within the brackets in the second line declares that the array will have five elements. In the case of our variable test_scores, we have now indicated that we need an array big enough to hold five different test scores. These five elements will be referenced with an offset from zero to four.

In the third line, the 0 represents the offset from the start of the array. In this case, the value 94 is being assigned into the contents of the first element of test_scores, which is at offset 0. Likewise, in the next line the value 89 is being assigned to the variable test_scores at offset, or sub, 1. The term sub is shorthand for subscript, which programmers often use in place of element. For example, the statement test_scores[3] is often read as “test_scores sub three.” In the last line of Example 10.3.1, a variable is used to access an element of the array. Although a constant or literal must be used in the brackets when the array is declared, variables can be used within the subscript operator to access a specific element.

When referencing a specific element, be careful not to go beyond the bounds of the array. There is nothing to stop you from doing so; however, you will not like the resulting problems. A few of the problems associated with going out of bounds follow.

1.  Incorrect data can occur—not just in the array but in other variables.

2.  The program could immediately terminate or crash with a runtime error.

3.  The program could crash upon exiting.

The first problem can occur because two variables for your program could happen to reside next to each other. If you go out of bounds of your array, you would overwrite the next memory location, thus changing the other variable. This is an extremely tough error to find, and it should be avoided at all costs. If it does happen, however, use your debugger to help find the error. Figure 10.3.2 shows how the array test_scores and the variable var_a might look in memory.

Notice that all subscripts are within the appropriate range, 0-4, in Figure 10.3.2. In Figure 10.3.3, however, we have made the mistake of accidentally overwriting one of our variables by referencing an element outside the bounds of the array.

In Figure 10.3.3, notice that test_scores[5] is out of bounds. The actual contents are indeterminate, and the execution of this statement will sometimes cause the following runtime error to be displayed:

images

images

Figure 10.3.2 Memory layout of an array and a single variable

 

images

Figure 10.3.3 Memory layout of an array that goes out of bounds

Both C and C++ were designed to be used by experienced programmers. As a result, the compiler assumes the programmer knows what he or she is doing, even if a mistake is sometimes made. The compiler will not provide a warning that you are about to create your own problem. As you can see in Figure 10.3.3, by taking a quick peek under the hood, we can see—and avoid—a potentially dangerous situation.

When your program executes and is placed in memory, it is given a specific area in memory for its use. Going outside this area demonstrates the second problem and causes your program to immediately crash with a runtime error. This type of error isn't too difficult to find. Simply step through the program until it crashes, stop debugging, and then run to the place where it crashed and examine all variables around that section of code.

The third error is probably one of the most difficult runtime errors to find. This happens when you go out of bounds of your array but it lands in an area of memory not used at this time. When the program exits, the area around your program is checked for integrity. If you have written into this unused area, the program will crash. The reason this is such a difficult error to find is that there can be a significant distance, in both time and space, between the introduction of the error and when we become aware of the error. If your program behaves in this manner, use the debugger to check each of your array subscripts to make sure they aren't out of bounds. As should be clear by now, a little extra time ensuring you don't go out of bounds of your arrays can save a lot of frustration and a considerable amount of time debugging.

Section 10.3 Exercises

Please respond to each of the following questions.

1.  To access a specific element in an array, you must use the ____________________ operator.

a.  subscript

b.  offset

c.  dimension

d.  modules

2.  The beginning element of an array is at offset ____________.

a.  0

b.  1

c.  2

d.  unknown

Based on the following array declaration, please respond to questions 3-6:

int sample[5];

3.  What would be the largest subscript you should use when referencing an element of the array sample?

a.  0

b.  1

c.  4

d.  5

4.  What would be the smallest subscript you should use when referencing an element of the array sample?

a.  0

b.  1

c.  4

d.  5

5.  How many different integers can be held in sample?

a.  0

b.  1

c.  4

d.  5

6.  Indicate whether each of the following statements is logically valid or invalid based on our previous declaration of sample.

images

7.  Indicate whether each of the following statements is true or false.

a.  An expression can be used as a subscript.

b.  The last element you should reference in an array is the same value used to declare the array.

c.  Making a reference to an element outside the bounds of an array is likely to cause the programmer a great deal of time and perhaps frustration.

d.  All of the elements contained within an individual array have the same physical size.

10.4 Initialization

Like any other variable, arrays can be initialized. The syntax of array initialization is as follows.

images

In the first syntax diagram, you can specify up to <number-elements> number of values. If you provide fewer values than the number of elements, remaining elements will be allocated but the values will be unknown or garbage. Some compilers, such as Visual Studio, may initialize the rest of the elements to 0; however, you should not depend on this happening. The second syntax uses the actual number of values in the braces to determine the size of the array. If there are 10 values, there will be 10 elements allocated for the array. Example 10.4.1 demonstrates how to initialize arrays.

images

Notice the declaration of frequency_counts only uses a single value for initialization. The first element will be initialized to 1, but in some environments, the rest of the elements will be initialized to 0.

STYLE NOTE The initialization method in which we explicitly specify the number of elements is the preferred approach, which makes it easy to determine the total number of elements allocated for the array. Calculating the number of elements when not explicitly specified requires you to either count the values or perform the calculation shown in Example 10.4.2.

images

Section 10.4 Exercises

Please respond to each of the following questions.

1.  What would be the value in numA[1] based on the following declaration?

int numA[5] = {0, 1, 2, 3, 4};

a.  unknown

b.  0

c.  1

d.  4

2.  Write a for loop to find the average of all the elements in the array numA in Exercise 1.

3.  What would be the value in numB[2] based on the following declaration?

int numB[5] = {0};

a.  unknown

b.  0

c.  1

d.  4

4.  What would be the value in numC[3] based on the following declaration?

int numC[5] = {0, 1, 2};

a.  unknown

b.  0

c.  1

d.  4

5.  What would be the value in num_elements based on the following code fragment?

images

a.  unknown

b.  4

c.  5

d.  6

6.  Write the necessary statement to create an array to hold the days in each of the 12 months of the year and to initialize each element with the correct number of days for each respective month (assuming a non-leap year).

10.5 Array Manipulation

With one exception, which we will discuss later in this chapter, you must read and write arrays one element at a time. As a result, the use of loops becomes a crucial tool in performing operations, especially those involving input and output. As illustrated in Example 10.5.1, for loops are a natural choice when manipulating arrays because they are generally used for specific numbers of iterations.

images

As you can see in Example 10.5.1, using a constant for the number of elements has become even more important. If the array was declared using a literal instead of a constant and you modified the array bounds, you would need to change each loop that is used to access the array. By declaring a global constant and using that constant in the condition of the loop, all you would need to do is change the constant declaration and recompile.

Another thing to notice in Example 10.5.1 is that this loop requires you to enter 25 test scores. However, many situations do not require the use of all the elements. Example 10.5.2 shows a solution to this problem.

images

In Example 10.5.2, the loop terminates either when the maximum number of values is entered or when the user chooses to quit. Regardless of which portion of the condition causes the loop to terminate, the variable num_values will hold the number of elements used in the array. Therefore, the variable containing the actual number of elements must be used when accessing the array, as shown in Example 10.5.3.

images

Section 10.5 Exercises

Please respond to each of the following questions.

1.  What would be displayed based on the following section of code?

images

2.  What would be displayed based on the following section of code?

images

Section 10.5 Learn by Doing Exercise

1.  Write a program that uses an array to store 10 elements, representing 10 students’ test scores that will be entered from the keyboard. Given the following grading scale, iterate through the array, determine the letter grade each score will receive, and store the grade in a corresponding character array.

images

Display the numeric score and the corresponding letter grade. Also calculate and display the class average as well as the number of students that receive each letter grade. For the purposes of this exercise, everything should be done from main.

10.6 Passing Arrays to Functions

Like any other variable, arrays can be passed to functions. But are they passed by value and reference like any other variable? Afraid not. Since the name of an array is associated with its beginning address, arrays are actually passed by pointer. Passing by pointer is very similar to passing by reference. The nuances between the two forms will not be discussed at this point, but Chapter 12 should clear up any issues regarding passing by pointer.

At this time all you really need to be concerned with is the fact that any changes made to the array inside the function are permanent and thus retained when the function ends. This is because you have the address of the array. A rule in C and C++ programming is that if you have access to the address of a memory location, you have the capability to change what is stored at that address. Since we pass the address of an array, the function can change any values in the array.

There is a very good reason why both C and C++ pass arrays by pointer. Think about how much overhead it would take to make a copy of a very large array. You would need to make a copy, which would not only take time but also double the memory usage. With passing by pointer, however, you only need to pass the address, which is four bytes. The problem with passing by pointer is that it becomes a little less safe. Functions could change the array when you really wouldn't want them to have that capability. Fortunately, as you will soon see, there is a way around this pitfall. Example 10.6.1 shows how arrays are passed to functions.

images

In Example 10.6.1, the brackets in the function declarations are empty. Although it is legal to specify the number of elements in the array, it usually isn't done. If you do specify the number of elements in the brackets, all references to the array size must match. Although it doesn't change the performance of your program, it is stylistically better not to include the array size in the header and declaration.

All of the functions in Example 10.6.1 are passed the beginning address of the array. However, what if you only wanted to pass one element of the array? This process is shown in Example 10.6.2.

images

Notice that the actual parameters are not arrays but one element in the array. Since each element in the array is an integer, passing a specific element of this array requires you to catch an integer. In Example 10.6.2, we passed the element by value to Fun1, but we could just as easily have passed the element by reference, as we did in Fun2. One common mistake is to place empty brackets in the call when passing arrays. This is syntactically incorrect; the only time brackets should be present in the call is when you are passing an element of the array.

To further demonstrate the process of passing arrays, we have provided a complete program in Examples 10.6.3 and 10.6.4.

images

images

images

images

Please spend some time examining the code in Examples 10.6.3 and 10.6.4. The code pulls all of the pieces from the previous sections into a fairly cohesive program.

One final point we need to make in relation to passing arrays to functions involves the use of the reserved word const. As discussed in Chapter 4, prefacing a variable with the word const indicates that once it is initialized, its value cannot be changed. When used with a formal parameter that is an array, const provides us with a method that prevents the body of the function from changing or altering the contents of our array. In essence, we have now achieved both the efficiency associated with passing our array by pointer and the safety of passing our array by value. Example 10.6.5 shows how we can make our array unchangeable by including the word const in both the function declaration and the function header.

images

images

In Example 10.6.5 it is invalid to try to change the array because it is caught as a const, which is slightly different than passing by value. Passing a variable by value allows you to make as many changes as necessary to the formal parameter—changes that just go away when the function ends. By catching the array with const, no changes can be made at all.

Another thing to be aware of is that it is illegal to return an array from a function. This makes a lot of sense if you remember that arrays can't appear on the left side of an assignment operator. Even if you could return an array, how would you catch it?

Section 10.6 Exercises

Please respond to each of the following questions.

1.  By default, an array is passed by ___________________.

a.  value

b.  reference

c.  constant

d.  pointer

2.  Which of the following would be the correct way to pass an array declared as int ids[10]; to the function Sample?

images

3.  What would be the correct way to write the declaration for a function called Sample that returns no value and receives an array of 10 integers called ids?

images

4.  Indicate whether each of the following statements is true or false.

a.  If an array is passed to a function, any changes or alterations made to the array within the body of the function are permanent and remain even after you leave the function.

b.  It is not possible to pass only one specific value or element of an array to a function.

c.  If you pass an array of integers to a function, you must catch an array of integers.

5.  Assuming we declared an array as int ages[5], explain what is happening in the following function call.

Sample( ages[2] );

6.  Based on the information provided in the previous question and assuming that the function returns no value, please write the function declaration for Sample.

7.  Explain the purpose of using the reserved word const in front of an identifier contained within a function declaration and header.

Section 10.6 Learn by Doing Exercise

1.  Rewrite the program in the Learn by Doing Exercise in Section 10.5. Break the program into functions so that it adheres to a good modular design.

10.7 Special Case: cStrings

Up to this point we haven't discussed how to read or store strings or how to write strings other than string literals, placing an obvious limit on what we've been able to do with our programs.

In order to add the functionality of strings to our programs, we will use a special form of a character array called a cString. A cString is a null-terminated character array that can be used to store strings. As we mentioned in Chapter 4, the null character—ASCII value 0—is represented as ‘’. When this character is placed within a character array, the array becomes a cString. Figure 10.7.1 demonstrates the use of the null character within an array.

Figure 10.7.1 shows how the variable first_name might look in memory containing the string “Willy”. Notice that first_name[5] contains the terminating null character, marking the end of the string.

images

Figure 10.7.1 Memory containing a cString

In the C++ world and in this text, the older C-style strings are often referred to as cStrings, since they are the only means by which C is able to use strings. C++ has an additional method, which we will cover later in this text.

When declaring a character array to be used as a cString, you must allocate one extra element to hold the null character. For example, if storing first names with an assumed maximum length of 15 characters, we would need to declare a character array with 16 elements, to allow room for the null character. As you will come to see, cStrings require special care and attention to ensure that there is room for the null. To reemphasize: cStrings require a terminating null character, but all other types of arrays—including character arrays not used as cStrings—do not have this requirement.

10.7.1 cString Initialization

At this point you could probably initialize a cString with the knowledge you have gained in the previous sections. A good guess might be what is shown in Figure 10.7.2; notice the inclusion of the terminating null character.

Although the method demonstrated in Figure 10.7.2 would work, it would be much better to initialize the array as shown in Figure 10.7.3. Notice that the memory used in storing our cString would be exactly the same, regardless of the method chosen.

images

Figure 10.7.2 Initializing a cString character by character

images

Figure 10.7.3 Initializing a cString using a string literal

10.7.2 I/O with cStrings

Fortunately, cin and cout work seamlessly with cStrings. On the surface, there isn't really much difference in how cin and cout deal with cStrings. Underneath the hood, however, these I/O routines behave a little differently, as shown in Example 10.7.1.

images

When used with cStrings, cout takes the address given and prints to the output buffer, character by character, until the null character is encountered. This is one reason why it is critical that the null character always be present in any cString. If there isn't a null character in the cString, cout will continue to print until you stop the program, the program crashes, or cout just happens to find a null character. Any of these situations is obviously undesirable.

The cin statement reads from the input buffer one character at a time, storing each in the cString variable. This continues until a whitespace character is read. The cin then places a null character at the next character location, terminating the cString.

One problem using cin is that it uses whitespace to delimit the input, therefore making it impossible to read a space from the keyboard. Another problem arises when the user enters too many characters at the keyboard. The cin statement will still read all of the characters up to the first whitespace, even if it means going out of bounds of your array. As discussed earlier, this is a highly undesirable situation.

There is another form of input that doesn't have the limitations of cin. This form allows you to specify the maximum number of characters to be read and also stops reading from the input buffer when the newline character is encountered or the maximum length, minus 1, is reached. This new form of reading input is shown in Example 10.7.2.

images

As you probably noticed in Example 10.7.2, .getline is a member function of cin, just as .width and .precision are member functions of cout. The .getline method reads from the input buffer until the carriage return is encountered or until the maximum number of characters, minus 1, is reached.

An optional form of .getline allows you to change the default delimiter from the carriage return to a programmer-specified character. This form is shown in Example 10.7.3.

images

images

In Example 10.7.3, an additional parameter has been added to the .getline member function. This parameter, ‘-’, changes the character that terminates the input from the default carriage return to the dash. Notice how this change affects the processing of the input. Reading from the input buffer stops as soon as the dash is encountered. Therefore, everything before the dash is stored in the cString whole_name, while the rest of the input is left in the buffer.

One potential problem with .getline is what happens if there is already a carriage return in the input buffer. The .getline function will read the carriage return, assume its job is done, and then continue on without the user being able to enter anything at the keyboard. So where could that carriage return come from? Many places, but the most common is from other cin statements. For example, if your program asks for your age, and you enter 20 and press the enter key to signify that you are done, all three keys that you pressed go into the input buffer. The 2 and the 0 are read from the input buffer and stored in your variable, but because cin is whitespace delimited, the enter key is left in the input buffer. Therefore, when .getline is executed, the enter key is the first character waiting in the buffer.

Another problem is created if the user typed too many characters at the keyboard the last time a .getline function executed. The maximum number of characters specified in the function call are read and stored in the cString variable while the rest of the characters are left in the buffer. Therefore, when the next .getline function executes, the user, once again, doesn't get to enter anything from the keyboard because .getline reads all of the existing information from the input buffer.

Is there a way to flush the input buffer like we saw with the output buffer in Chapter 5 in relation to the .flush member function or the flush manipulator? Yes, but unfortunately not nearly as simple. The code in Example 10.7.4 will flush the input buffer before and after the .getline function call.

images

images

In Example 10.7.4, we flush the buffer before and after our call to .get-line. The first .ignore cleans the buffer prior to the .getline call. The second flush is used to clean any remaining characters in the buffer after the .getline function has executed. What exactly is that nasty looking line of code doing? Unfortunately, any detailed explanation at this point would probably be meaningless. The simple explanation is that the line of code flushes the input buffer. A little more detailed explanation is that the .ignore member function removes a certain number of characters from the keyboard buffer. Everything in the parentheses for the .ignore function call allows us to find the number of characters currently in the keyboard buffer. Although not intuitive, .getline sets a flag if it reads the maximum number of characters. If this happens, the .clear function call is required to reset any flags that may have been set by the .getline function.

Based on the previous discussion, it may appear that .getline is more trouble than it's worth. In reality, however, .getline is much safer for reading cStrings from the keyboard than cin is.

10.7.3 cStrings and the “address-of” Operator

When explaining how cout worked with cStrings, we stated that cout takes the address given, prints one character at a time starting from that address, and continues until the null character is encountered. Therefore, if we could get the address of one of the elements other than the first, we could print a portion of the cString. The operator used to do this is called the address-of operator and is represented by a single ampersand (&).

Don't confuse the address-of operator and the reference operator. The address-of operator returns an address of its operand, which, by the way, can be any identifier. The reference operator is usually associated with passing arguments to functions and makes an alias to another variable. At this point, a reference operator will only appear in the header or declaration of a function.

Example 10.7.5 illustrates how to use the address-of operator to print a portion of a cString.

images

Section 10.7 Exercises

Please respond to each of the following questions.

1.  A null-terminated character array is referred to as a ___________________.

a.  variable

b.  cString

c.  vector

d.  problem

2.  Assuming you wanted to create a cString to hold a maximum of 15 characters for an ID, how big would you need to declare your array?

a.  14

b.  15

c.  16

d.  unknown

3.  Which of the following statements would correctly declare and initialize our cString array called name?

images

4.  Indicate whether each of the following statements is true or false.

a.  The cin statement reads from the input buffer one character at a time until a whitespace character is encountered or read.

b.  When reading data from the keyboard, cin places a null character immediately after the last character read, thus terminating the cString.

c.  Assuming that both name1 and name2 are declared as cStrings of equal length, the following statement would copy the contents from name2 to name1.

name1 = name2;

d.  Assuming you declared a cString called name1, the following statement is valid.

name1 = “Jamie”;

e.  One of the most important things you must remember is not to reference an element of an array outside the bounds of the array.

5.  When using cout with cStrings, the output buffer is printed character-by-character until ___________________ is reached.

a.  the end of the array

b.  a space character

c.  a period character

d.  the null character

6.  What is the name of the method for reading characters from the input buffer until either a carriage return is encountered or the maximum number of characters, minus one, is achieved?

a.  cin

b.  cout

c.  .getline

d.  .ignore

7.  What is the overall purpose of the rather complex line of code that follows?

cin.ignore( cin.rdbuf()->in_avail() );

a.  flush the input buffer

b.  flush the output buffer

c.  clear out a cString array

d.  impress your friends

8.  Declare a cString called first_name, one called last_name, and another called temp_name. Make sure they can each hold up to 15 individual characters. Initialize first_name to your first name and the other two cStrings to 0.

Section 10.7 Learn by Doing Exercises

1.  Write a program that prompts for the user's first and last names. Use the toupper function to ensure that the first character of each name is uppercase. Then display a cordial greeting including their whole name.

2.  Write a program that prompts for the user's first and last names. Declare a third array that will hold the user's last name and first name, separated by a comma and a space. Use loops to iterate through the names one character at a time, storing them into the full name array. Don't forget about the terminating character.

3.  Modify the Learn by Doing Exercises from Sections 10.5 and 10.6. Add a function that prompts for the course number (e.g., CST 116) and your professor's name (e.g., Dr. Sherry Yang). When the results of the program are displayed, use the new information as part of the report's title.

10.8 cString Functions

There are some limitations as to what we can do with cStrings because of the way they are implemented in C++. As discussed earlier, it is syntactically incorrect to place a cString on the left side of an assignment operator. So how do we place strings into cString variables other than through initialization? There are many predefined cString functions accessible through the <cstring> header file that perform some of these basic manipulations. These functions all expect at least one cString parameter. The following sections discuss some of the more popular cString functions available. Check your online help for more functions.

In the previous discussion, we stated that there was some limitation as to what we can do with cStrings and operators. These limitations arise because the name of an array references its beginning address. Example 10.8.1 illustrates the incorrect method of comparing two strings using the relation equality operator.

images

images

Even though the two string values are the same, the relational operator compares the two addresses, not the strings. Since the arrays will never have the same address, this statement will always be false.

10.8.1 Copying

As already stated, we can't assign a cString or string literal to another cString. We can, however, use a couple of predefined functions to accomplish this task. The basic syntax follows:

images

Both functions copy up to and including the null character from the source to the destination. The destination must be a cString variable, but the source can be either a cString or a string literal. The second function differs from the first in that it needs a third parameter—an integer value specifying how many characters to copy from the source to the destination. Example 10.8.2 illustrates both the strcpy and the strncpy functions.

images

images

10.8.2 Appending

We can also use a couple of functions to append one cString or string literal to another cString.

images

These functions behave in much the same manner as the copy functions. The only difference is that these functions concatenate, or append, the source onto the end of the destination. Remember: the second function will only append the number of characters specified from the beginning of the source onto the end of the destination. Example 10.8.3 uses strcat to append one cString onto another.

images

images

10.8.3 Comparing

If we used relational operators to compare two cStrings, all it would do is compare the addresses of the two operands. Typically, it is necessary to compare the contents of two cStrings or string literals, not the addresses. Four functions can be used to accomplish this, the syntax of which follows:

images

All of these functions return one of three different values, as shown in Table 10.8.1.

So how can one cString be less than another? By comparing ASCII values. These functions compare character by character until they encounter a difference between two characters. The functions then compare the ASCII values of the two characters. That is how the results of the table are computed.

The i in the stricmp and strnicmp functions means to ignore the case while doing the comparisons. The following examples illustrate the use of the compare functions. Please review each of the examples and the related output. Example 10.8.4 demonstrates the strcmp function.

Table 10.8.1 Return values from compare functions

Return Value Relationship of str1 to str2
< 0 str1 less than str2
0 str1 equals str2
> 0 str1 greater than str2

images

Example 10.8.4 demonstrates that the two names are not equal. Furthermore, since the two names differ at the third character, the m has a greater ASCII value than the i. Therefore, string_1 is greater than string_2. Example 10.8.5 demonstrates the strncmp function.

images

images

In Example 10.8.5, the output specifies that the two strings are identical. This is because we only compared the first three characters. If we had used strcmp, string_1 would have been greater than James. Example 10.8.6 shows how the case of the characters is related to the ASCII chart.

images

The results in Example 10.8.6 may not be what you expected. We tend to think of uppercase characters as being bigger than lowercase characters. Remember, however, that one of the oddities of the ASCII table is that upper-case letters come before lowercase letters. Therefore, a lowercase m has a greater value than an uppercase M. Example 10.8.7 shows how to accomplish a case-insensitive comparison.

images

Example 10.8.7 uses the stricmp function to achieve a case-insensitive comparison. All of these comparison examples show the nuances involved in comparing strings. These functions will become valuable tools for you in the near future.

10.8.4 Finding the Length

Another useful function is strlen, which returns the number of characters preceding the null character. The syntax of the strlen function is:

<size_t-variable> = strlen ( <cString< );

The return type of the strlen function is a variable of type size_t. This data type equates to an unsigned int, which makes sense because we can never have a negative length. Example 10.8.8 shows a couple of different uses associated with the strlen function.

images

10.8.5 Changing the Case

We introduced a couple of functions, toupper and tolower, in Chapter 9 that allow you to change the case of a single character. There are a couple of functions that perform the same task but on an entire cString. The syntax of these functions is as follows:

images

Notice that unlike toupper and tolower, these functions permanently change the parameter itself. Example 10.8.9 demonstrates both the strupr and the strlwr functions.

images

10.8.6 Reversing

Another function that is sometimes useful is the strrev function. This function reverses the cString and has the following syntax:

strrev ( <cString> );

Like strupr and strlwr, strrev permanently changes the actual parameter. Therefore, the actual parameter must be a cString—not a string literal. Example 10.8.10 uses several cString functions, including strrev, to determine whether a string is a palindrome—spelled the same forward as backward.

images

images

10.8.7 Converting cStrings to Numbers

Other often-used functions allow you to convert strings to numbers. The three functions are shown here:

images

These functions convert the parameter until they reach a character that is not valid for the return type. Example 10.8.11 uses atof as a model to illustrate the use of the previous three functions. Notice the impact of the comma used within string_3.

images

The third conversion in Example 10.8.11 stops when the process reaches the comma. The computer considers the comma an invalid character, which stops the conversion. The comma separator in numbers is for human readability and is not necessary, or desirable, for the computer. Although we only demonstrated the use of one conversion function, all of the others work the same way.

10.8.8 Converting Numbers to cStrings

Two functions that perform the opposite of atof, atol, and atoi are the ltoa and itoa functions. These functions convert numbers to cStrings and have the following syntax:

images

The parameters passed to these functions should be fairly obvious, except for <radix>. This parameter indicates to which base the number should be converted. For example, the value 16 for the radix converts the number to a hexadecimal value. The radix can be any value from 2 to 36. As discussed in Chapter 1, hexadecimal values use the characters 0-9 and A-F to implement a base 16 number. Base 36 extends this concept to include 0-9 and A-Z to represent numbers. Example 10.8.12 demonstrates the use of the itoa function to convert a number to a cString.

images

images

Notice that there isn't a function to convert a float or a double to a cString. You will have to write your own!

10.8.9 Summary of cString Functions

In the previous eight sections we presented a variety of very powerful functions for manipulating cStrings, although there are a few additional functions we didn't cover. Table 10.8.2 is designed to act as a quick reference, summarizing the string-related functions as discussed.

Table 10.8.2 cString functions

Function Purpose
strcpy ( <dest>, <source> ); Copies source to dest
strncpy ( <dest>, <source>, <n> ); Copies n characters from source to dest
strcat ( <dest>, <source> ); Appends source onto dest
strncat ( <dest>, <source>, <n> ); Appends n characters from source onto dest
<int> = strcmp ( <str1>, <str2> ); Compares str1 to str2
<int> = strncmp ( <str1>, <str2>, <n> ); Compares first n characters from str1 to str2
<int> = stricmp ( <str1>, <str2> ); Compares str1 to str2 with case insensitivity
<int> = strnicmp ( <str1>, <str2>, <n> ); Compares first n characters from str1 to str2 with case insensitivity
<size_t> = strlen ( <cString> ); Returns length of cString
strupr ( <cString> ); Converts all characters to uppercase
strlwr ( <cString> ); Converts all characters to lowercase
strrev ( <cString> ); Reverses the characters
<double> = atof ( <cString> ); Converts characters to a double
<long> = atol ( <cString> ); Converts characters to a long
<int> = atoi ( <cString> ); Converts characters to an integer
ltoa ( <long_var>, <cString>, <radix> ); Converts long integer to cString using radix (base of long_var)
itoa ( <int_var>, <cString>, <radix> ); Converts integer to cString using radix (base of int_var)

Section 10.8 Exercises

Please respond to each of the following questions.

1.  To access the various predefined functions for manipulating cStrings, include the ___________________ header file.

images

2.  We refer to the process of doing a character-by-character comparison as _____________________.

a.  dangerous

b.  lexical analysis

c.  sorting

d.  examination

3.  Appending one string onto another is called _______________________.

a.  dangerous

b.  concatenation

c.  adding

d.  extending

4.  Whenever you copy one string to another, it is imperative that the ______________ cString has the room needed to hold all the characters and the required null terminating character.

a.  starting

b.  source

c.  destination

d.  base

5.  Use the appropriate function to find the length of tester and assign that value into the variable

images

6.  Use the appropriate function to copy all of the contents of the cString variable string_1 to the variable temp_first.

images

7.  Write the necessary section of code to compare the first six characters of string_1 to string_2. If the strings are the same, simply display ‘Same’; otherwise, display ‘Different’.

images

8.  Use the appropriate function to convert all the letters in last_name to uppercase.

char last_name[] = “Smith”;

9.  Use the appropriate function to convert the contents of the variable a_grade to a double, assigning the results to grade_holder.

images

10.  Use the appropriate function to reverse all the characters in the variable palindrome.

char palindrome[] = “Can I attain a C”;

11.  Use the appropriate function to convert var_a into hexadecimal, placing the results in hex_value.

images

10.9 cString Function Standards

Many of the functions discussed in Section 10.8 are not ANSI standard functions. Functions such as strlwr, strupr, and stricmp are very useful functions provided by Microsoft with its Visual Studio compiler. If you are developing with a different IDE, you may or may not have these functions available to you. If you don't, it might be a good idea to write your own versions.

Another issue that already occurs in Microsoft Visual Studio is that many of the string functions are flagged as deprecated. A deprecated function is one that has a newer function to take its place. For example, Microsoft would rather you use the strcpy_s function than the older strcpy function. The newer function has a different syntax that provides a more secure functionality. This has been done to alleviate the problems with buffer overrun errors and attacks.

The current ISO standard does not contain definitions for these more secure versions of the cString functions, but the ISO is working on including these newer functions for future standardization. It is our belief that the older functions are still a valuable part of any new C or C++ programmer's repertoire of routines. There are many millions of lines of code in existence that use the older functions. These programs will still need to be maintained and upgraded, so knowledge of these older functions will not be wasted. Also, once you understand the functions listed in this chapter, it is a relatively easy task to learn the newer functions.

Those of you using the Microsoft compiler will receive a warning anytime one of these deprecated functions is used. To use the older functions without receiving a warning, include the following line of code at the beginning of your source code.

#define _CRT_SECURE_NO_DEPRECATE

Some functions—such as strrev and stricmp—may have newer implementations that can be accessed by placing an underscore at the beginning of the function. For example, strrev would become _strrev. Except for the underscore, the syntax and capabilities of these functions are exactly the same.

As you can see, the status of these functions is in a state of flux. However, you shouldn't be discouraged or disappointed about this; the functions discussed in this chapter are still widely used.

10.10 Multidimensional Arrays

At this point, many of our students ask, “Can you have an array of arrays?” Yes. “Can you have an array of arrays of arrays?” Yes. “Can you have an array of arrays of arrays of arrays…?” Yes! In C and C++, this is how multidimensional arrays are implemented.

C and C++ allow you to have as many dimensions as you want. However, after three or four dimensions, the ability to visualize the array becomes rather stretched and often its usefulness is replaced with utter confusion. It is easy to visualize a two-dimensional array as a simple grid made up of rows and columns. Figure 10.10.1 shows one method of visualizing a two-dimensional array.

In Figure 10.10.1, the array called last_name is made up of 3 rows and 6 columns, for a total of 18 elements. The upper left element would be referred to as last_name[0][0], while the shaded section is located at last_name[1][2].

Since this is indeed an array of arrays, if we access a row of a two-dimensional array, we are accessing an address. This is a key point to remember, especially when we start demonstrating how to create an array of cStrings.

Three-dimensional arrays are also easy to visualize, this time as a cube, with the added dimension supplying the depth of the cube. The fourth dimension is harder to visualize and often deals with time. We will leave the visualization of a four-dimensional array as an exercise for you!

images

Figure 10.10.1 Visualizing a two-dimensional array

images

Figure 10.10.2 Another example of visualizing a two-dimensional array

images

Figure 10.10.3 Memory layout of a two-dimensional array

We have presented one approach to visualizing multidimensional arrays; however, this is not how they are stored in memory. Multidimensional arrays are stored as a contiguous block of memory, as stated by the definition of an array. Therefore, there needs to be an algorithm to figure out how the arrays are stored in memory. Picture a two-dimensional array, as shown in Figure 10.10.2.

This method of visualizing an array is similar to what you have previously seen. In this case, we are creating an array called test_scores made up of five rows, referenced 0-4, and three columns, referenced 0-2.

C and C++ store arrays in what is called row-major order. This means the first row is stored in memory, then the next, and so on. Figure 10.10.3 shows the array from Figure 10.10.2 as it might appear in memory. Notice how the memory associated with the entire array is contiguous, similar to the memory for a one-dimensional array. In this example, the array test_scores has enough room for five students with three test scores each.

Using the array in Figure 10.10.3, we can extrapolate the following formula to find a specific element.

[desired_row * total_columns + desired_column]

Therefore, if we wanted to find test_scores[2][1], it would be similar to test_scores[2 * 3 + 1], or test_scores[7]. Logically we can now see that the desired element is the eighth element. However, it is important to always use two sets of brackets when accessing a specific element in a two-dimensional array.

Not all languages store their arrays in row-major order; some use column-major order instead. In column-major order, an entire column would be stored before storing the next.

So in what type of scenario could we use these multidimensional arrays? As we saw in previous sections, we can hold one string in a single-dimensional array—for example, the last name of one student. With a two-dimensional array, we could hold the last names of all the students in one course. A three-dimensional array could hold the last names of all the students in all the courses taught by a single professor. A four-dimensional array could hold the last names of all the students in all the courses taught by a single professor for all terms in an academic year.

Although multidimensional arrays can be used in this way, there are better tools to accomplish these tasks. This text, therefore, will focus on single- and two-dimensional arrays.

10.10.1 Declaration

To declare a two-dimensional array, you need to simply specify the number of rows needed for the array via another set of brackets. The syntax of the declaration of a two-dimensional array follows:

<data-type> <array-name> [ <rows> ] [ <columns> ];

Even though we said we would focus on two-dimensional arrays, it is easy to extend this syntax to declare a three-dimensional array:

<data-type> <array-name> [ <depth> ] [ <rows> ] [ <columns> ];

Example 10.10.1 shows a variety of multidimensional array declarations.

images

In Example 10.10.1, the first example declares three separate two-dimensional arrays for a group of 20 different students. In the second example, we create an array large enough to hold the 12 monthly sales for two different regions for 20 different salesmen.

10.10.2 Initialization

Initialization is accomplished in much the same manner as with a single-dimensional array, but with a few more curly braces. This process is demonstrated in Example 10.10.2.

images

If you do not initialize any of the elements in the array, all of the elements will be garbage. Figure 10.10.4 shows the contents from the Watch window of the uninitialized version of the array parents_age.

10.10.3 Multidimensional Array I/O

In a previous section, we demonstrated how a loop is often used in reading, manipulating, or displaying the contents of arrays. You've probably already guessed that in order to fill or print a multidimensional array you need to use nested loops. This technique is demonstrated in Example 10.10.3.

images

Figure 10.10.4 Uninitialized array elements

images

images

Multidimensional arrays of cStrings are a little easier to manipulate because they do not require the use of nested loops, as shown in Example 10.10.4.

images

10.10.4 Passing Multidimensional Arrays

Passing multidimensional arrays is nothing more than an extension of what we've already learned about passing arrays. The difference is that only one set of brackets can be left empty. In a two-dimensional array, the rows can be left empty but the number of columns must be specified. When passing arrays, only the first set of brackets can be empty; all other sets must contain values. This is because all that the functions really receive is the beginning address. Requiring us to specify the number of columns in a two-dimensional array allows the function to determine the beginning of each row. That way we can use the double subscript operator to access the elements of the array. This is demonstrated in Example 10.10.5.

images

Section 10.10 Exercises

Please respond to each of the following questions.

1.  Indicate whether each of the following statements is true or false.

a.  In C and C++, you can have a maximum of three dimensions in an array.

b.  When trying to visualize a two-dimensional array of integers, it is best to picture a grid made up of rows and columns.

c.  Similar to single-dimension arrays, multidimensional arrays are physically stored internally in a contiguous block of memory.

d.  Similar to single-dimension arrays, multidimensional arrays by default are passed to functions by value.

2.  When we reference an individual element within a two-dimensional array, we use the first subscript to represent the _______________ and the second subscript for the ________________.

a.  row / column

b.  column / row

c.  index / minor

d.  row / minor

3.  If an array of integers or doubles has three dimensions, how many subscripts are needed to reference an individual element?

a.  2

b.  3

c.  4

d.  unknown

4.  Declare a two-dimensional array called l_name that can hold the last names of 10 students, each having a maximum length of 12 characters.

5.  Declare an array called sales that contains a company's unit sales for the past twelve months. There are 30 salespeople in the company.

6.  How many elements are contained within the following array declaration?

int check_it[10][21];

7.  Write the necessary statements to declare a two-dimensional array called names and initialize the first row to Sue and the second row to Marie. Assume that the longest name will be 15 characters and that we will not have more than 10 names.

8.  Write one statement to print out both the first and the second person's name in the array names created in the previous question.

9.  Write the necessary statements to create an array to hold three different integers representing quiz scores for a class of 25 students. Initialize each element of the array to 0.

10.  Using the array you created in the previous question, assign the first quiz score for the second person in the array the value 91. Assign the third quiz for the fourth person the score of 88.

11.  Assume the following variables have been declared and the siblings array has been populated with the appropriate data.

images

a.  Write two separate and unique for loops to:

i. Add up the values of all the elements contained in the first column, putting the results in the total_sisters variable.

ii. Add up the number of siblings each person has, putting each individual's result into the array called ind_siblings_total.

b.  Write the necessary statement to pass the array called siblings to a void function called Foo.

c.  Write the function declaration for Foo.

d.  Write a for loop (or loops) to neatly print out all the elements of the array called siblings. Make sure you include some space between each value and print each individual's data or record on its own line.

10.11 Parallel Arrays

Up to this point we have helped foster your understanding of arrays by illustrating different methods you could use to visualize how arrays might look, at least from a conceptual point of view. One additional concept we need to introduce is called parallel arrays.

Within our programs we often use a number of separate but related arrays for holding data. Suppose for example that there is a need to hold the information for five students. It would be necessary to have an array for holding the student ID, an array for the last name, another for the age of each student, and one last one for each student's gender. If you think of these four arrays as being related, then the first element of each of the arrays would be the data for a specific student. Likewise, the second element of each array would contain the information for a different student. These four separate but related arrays are often referred to as parallel arrays, as illustrated in Figure 10.11.1.

If you are referencing the student with the ID of 1010 in Figure 10.11.1, he is a 21-year-old male with the last name of Jones, based on the relationship of the first array to the other three. While C++ does not provide any built-in functionality for working with parallel arrays, it is relatively easy for the programmer to manually synchronize the subscript between the different arrays.

images

Figure 10.11.1 Parallel arrays

Section 10.11 Exercises

Please respond to each of the following questions.

1.  Based on the data in the parallel arrays contained within Figure 10.11.1, what is the last name, age, and gender of the individual with the ID of 1040?

2.  A record is made up of one or more related fields and contains all of the data associated with a specific entity.

True False

Section 10.11 Learn by Doing Exercise

1.  Write a complete modular program that accepts the following information from the keyboard:

Table 10.11.1 Sample data

Student Club President Number of Students
Computer Systems Society Kim Cares 49
Society of Women Engineers Jeanie Queen 51
Sigma Tau Gamma Storm Drain 241
Trekkies C. Kirk 230
Home Brewers Ross Coe 15
High Altitude Ballooning Justin Time 19
Rugby Ryan Johns 25
IEEE Marc Bansmere 36
International Club Kong Mbenkum 102
Dance Club Will Shaver 64

Also, the university gives each club $75 for every student in the club. Create another array to hold the amount of money received from the school. Please display all of the information in a neat, tabular format. Don't forget the column headings as well as appropriate formatting for the data.

10.12 Problem Solving Applied

Our objective is to write a program to read and store a group of student records, process the data, and print the results. This is fundamentally the same problem we used in the past couple of chapters, but now we employ arrays to help us in our solution. If necessary, please refer to Section 8.6 and Section 9.10 to review the problem specifications.

In earlier chapters we introduced and discussed the concept of I(nput) →P(rocess) →O(utput). Based on our years of teaching, we have found that this model often works well when used by beginning programming students in designing the overall flow or structure of their programs. It is especially useful when the solutions involve the use of arrays.

Employing this technique in the design of our current project and the related use of arrays causes us to make some slight modifications to the previous structure chart, as shown in Figure 10.12.1. Although main continues to act as a driver for the overall flow of our program, the major functions are clearly broken up into three main categories: input, process, and output. Within the input phase, we will focus on reading or entering the data into our arrays. Once we have loaded or entered all of the data, we will move into the second major phase, processing. Finally, when all of the necessary processing is complete, we will iterate through our arrays to print the necessary data and information, thus completing the output phase.

Using this approach allows us to isolate the various components of the programming problem into three main sections. By focusing on only one specific section, or even a subsection, at a time, we can more easily identify the specific functionality needed to solve the problem.

During the design of the individual components, make sure to focus only on the task or function at hand and not the entire problem. Indeed, the ability to isolate and break down the major tasks and concentrate on the related details is one of the real advantages to using this model. This technique, as you may remember, is also the key to incremental programming.

Notice how each of the three sections act as individual drivers for their own respective and related tasks. Control is transferred into each of these sections from main, so it will later become imperative that you give some careful thought to where you define your variables to provide the appropriate levels of access. The reorganization of the overall functionality of our program was done in an effort to make it easier to visualize, identify, and develop the specific tasks we need to accomplish.

images

Figure 10.12.1 Structure chart

Now that the structure chart has been created, we will use this diagram to help develop the pseudocode shown in the design phase.

Design:

images

images

images

Some programmers would argue that the functions ProcessData and PrintReport are unnecessary because the function calls that make the body of these functions could be placed in main. While this would be even more efficient, it is our belief that the current structure clearly identifies each phase of the IPO model and allows for more extensible code if we need to add other calculations or reports.

Like always, this pseudocode can now be translated into C++ source code. In Section 10.17, our colleague Troy will provide us with some start-up code that will basically implement the preceding algorithm.

One additional feature you may have already discovered within the Microsoft IDE is the small box on the left side of the area where you type your code. Within this box you will find either a ‘+’ or a ‘-’ symbol. Clicking on the symbol will either collapse or show the body of the function or comment. This can be especially helpful when you have a function you know is working and you no longer need to continue scrolling through the listing. If you want to see the contents of the function or comment, you can either click the box or maneuver your cursor over the grayed-out section to the right that contains the ellipses (…). If you have not explored this feature, please make it a point to do so now.

10.13 C—The Differences

Although there is a lot of information in this chapter, there aren't very many differences between C and C++. In C, cStrings are just called strings, and you need to include <string.h> in order to use the functions from Section 10.8.

As always, input and output between C and C++ is radically different. Therefore, you are not able to use cin.getline in a C program. Also, since scanf is our primary input function, we need to discuss how to use this function to read strings.

Remember, scanf requires any variable to be passed by pointer. In other words, scanf requires the address of the variable to store the information. With regular variables, you are required to use the address-of operator to send the address to scanf. However, since the name of the array is already an address, the address-of operator is not needed when reading strings. Also the format specifier for a string is the %s, as we saw in Chapter 5. Example 10.13.1 shows a couple of examples of reading strings from the keyboard.

images

images

The problem with scanf, as with cin, is that you can't have strings with spaces as input. However, there is a counterpart to cin.getline called gets. The major difference between the two routines is that gets does not specify the number of characters to be read. The good news is that the routine to flush the buffer is much simpler. The gets and flush routines are shown in Example 10.13.2.

images

10.14 SUMMARY

This chapter has been devoted to the exciting and powerful concept of arrays. We first examined how, through the use of arrays, we can relate or connect numerous pieces of data with one specific variable name. These arrays hold a set of contiguous elements, or pieces—all of which are the same data type and are referenced by the array's name, which is also its beginning address. Because of this organization, we can easily and quickly manipulate arrays.

To gain access to a specific element in our arrays, we examined the subscript operator. The subscript value can be any integer variable, constant, or literal, and allows us to specify an offset from the beginning of the array, with the starting element being 0. Be sure to avoid going out of bounds of any array. We will have more to say about this in Chapter 12, but for now just remember that the individual elements in an array are physically located next to each other in memory.

When passing arrays, keep in mind that arrays are passed by pointer. Therefore, the function can change the original data, similar to passing by reference. If necessary, you can use the reserved word const to stop the array from being altered from within a specific function.

Another important concept introduced in this chapter revolved around a special form of character arrays called cStrings. As you may recall, cStrings must be null terminated and have some unique requirements and functions for using and manipulating their contents. Functions such as strcpy, strcat, strcmp, strupr, and strlwr are all available to provide some significant functionality when working with cStrings.

After your understanding of one-dimensional arrays became more solidified, you had the necessary foundation required to extend that concept and discovered the power and application of multidimensional arrays. We ended our discussion by presenting the topic of parallel arrays and how they aid in coordinating and visualizing data.

10.15 Debugging Exercise

Download the following file from this book's website and run the program following the instructions noted in the code.

images

images

images

images

10.16 Programming Exercises

The following programming exercises are to be developed using all phases of the development method. Be sure to make use of good programming practices and style, such as constants, whitespace, indentation, naming conventions, and commenting. Make sure all input prompts are clear and descriptive and that your output is well formatted and looks professional.

1.  Write a program that includes the following three functions, making sure each is clearly exercised within your program:

a.  IsPalindrome : This function returns true if the cString parameter is a palindrome, false if it is not. As you have seen, a palindrome is any group of characters (i.e., a word) that appears the same when written forward and backward.

b.  IsAlphaStr : A function that returns true if the cString you pass it contains all alphabetic characters. If the parameter does not contain all alpha characters, the function returns false. Feel free to use the isalpha function we discussed earlier.

c.  CountChar : A function to simply count and return the number of times a specific character occurs in a cString. This function will take two parameters: the cString to check and the specific character you are looking for.

2.  For this exercise, you will write a program to convert numbers to different bases. For example:

FF16 = 25510 = 3778 = 111111112 = C321 = 73 36

Your program will prompt the user to enter a number and the base of the number. It will then ask what base to convert the number to. Your program will handle bases 2-36.

Your program should include the following features:

images  If the user enters a base outside the range, display an error message and make the user reenter the base.

images  If the number doesn't match the base the user typed in (i.e., the character A is invalid for a base 10 number), display an error message and have the user reenter all of the information again.

images  The number displayed to the user should be displayed in uppercase.

images  Be sure your output is self-documenting. For example:

        FF (base 16) = 255 (base 10).

Hints:

images  The maximum digit that can be in a number is one less than its base. For example, 7 is the maximum digit in a base 8 number, and 1 is the maximum digit in a base 2 number.

images  Convert all numbers to base 10 first and then convert them to the desired base.

images  Since the number is entered into a character array, each digit is a character. You must convert this ASCII value to the appropriate value for that base. For example, A is value 10, B is value 11; to calculate these values, use the ASCII value for the alphabetic character and subtract 55. This works because A is ASCII value 65, B is ASCII value 66. If the character in the array happens to be a digit (i.e., numeral), use the same philosophy but subtract 48.

If you are unsure how to change bases, please review Chapter 1.

3.  Assume you are employed by a small company to help track the sales for four salespeople, each of whom sells three different products. Along with the salesperson's ID, you will ask the user to enter the dollar sales for each of the three products sold last week. Once the user has entered the data for all four salespeople, your program should generate a table that prints out the salesperson's ID, his or her individual sales for each product, and the individual's overall sales. In addition, the program should print the overall percentage of the total sales that each individual's total sales represent and, at the end of your report, print the total sales for each respective product and the overall sales total. An example report format follows:

ID Prod1 Prod2 Prod3 Total Percent
1234 10.23 15.37 20.50 46.10 xx.xx%
______________________________________
Totals xx.xx xx.xx xx.xx xx.xx

Use a two-dimensional array to hold the sales information.

5.  Design and implement a menu-driven program that will take two two-dimensional arrays, or matrices, of floating-point numbers and perform mathematical operations on them. The matrices will all be 3 × 3. The operations will be selected from a menu with the following options:

images Get new matrices

images Multiply matrices

images Add matrices

images Exit

Make sure the output is readable and self-documenting. The examples that follow provide patterns for your output.

Matrix addition can only be performed if the dimensions of the matrices are the same. This is not a concern since we are assuming all of the matrices are the same size. An example of matrix addition follows:

images

Matrix multiplication is much more complicated. An example of matrix multiplication follows:

The formula cij = ai1b1j + ai2b2i + ai3b3j creates a resulting matrix in which a and b are matrices, i is the row number, and j is the column number.

images

images

Although there are many opportunities to do so in this program, be careful not to introduce redundant code. If you want to copy and paste a section of code, think about making it a function.

5.  Write a program that creates and displays Pascal's triangle. Pascal's triangle is used in the manipulation of polynomials. A basic Pascal's triangle is shown here:

images

As you can see, the outside edges are always 1. The inside numbers are computed by adding the two numbers from the row above.

Pascal's triangle is used is calculating the coefficients of the x terms in a polynomial. For example, if we have the following mathematical expression: (x + 1)3, the resulting polynomial would be: x3 + 3x2 + 3x + 1. Notice the coefficients of the x terms are: 1, 3, 3, 1, which are also the same values in the fourth row of Pascal's triangle.

Create a program that asks for a maximum value from the user. Then calculate and display the resulting triangle. Note: The preceding triangle was created for the user input of the fourth power.

10.17 Team Programming Exercise

Do you remember good old Troy from earlier chapters—the guy who recently joined your development team? Well Troy has once again started a project that you will need to finish. The code that follows is his first attempt at extending the solution to the problem presented in Section 10.12. Basically, you now need to make sure the code can be built and executed. Unfortunately, there appear to be some problems with it as it now stands. After you get the code working, you will need to add two additional functions and of course pass in the necessary arrays. The first function simply iterates through the array of individual averages and counts the number of A's, B's, C's, and so on. Remember: This function should be called from the ProcessData method provided.

After you have printed the totals on the report, call the other new function you wrote that will generate a graph showing the frequency of the individual letter grades (A-F) for the class.

Your graph should look somewhat like the following:

Grade Frequency Graph

A  ***

B  ****

C  *******

D  ***

F  **

Before you get too carried away, get the code to compile. Make sure you carefully review and correct any errors before adding the additional functionality. In your review you will notice that Troy has put some comments within the code, asking for an additional feature or two. Please make it a point to implement those as well. Also, Troy wasn't consistent in his formatting. Correct the formatting so it is consistent and conforms to an appropriate style. Once you have the code compiling, we suggest you develop the relatively short pseudocode for each of the two new functions. Once you have the pseudocode, you should find it relatively straightforward to write and implement the new functions.

And one last thing: Troy said he should probably print the graph as shown here. If time permits, write the code to generate the new style report. Provide the option to display the graph in either of the two formats.

Grade Frequency Graph

images

images

images

images

images

10.18 Answers to Chapter Exercises

Section 10.2

1.  c.  name

2.  b.  5

3.  b.  1

4.  a.  false

b.  true

c.  false

5.  c.  constant

Section 10.3

1.  a.  subscript

2.  a.  0

3.  c.  4

4.  a.  0

5.  d.  5

6.  a.  valid

b.  valid

c.  invalid—out of bounds

d.  valid

7.  a.  true

b.  false

c.  true

d.  true

Section 10.4

1.  c. 1

images

3.  b.  0

4.  b.  0

5.  d.  6

6.  int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

Section 10.5

1.  A B C

2.  100

Section 10.6

1.  d.  pointer

2.  a.  Sample( ids );

3.  c.  void Sample( int ids[] );

4.  a.  true

b.  false

c.  true

5.  We are passing only one individual element of the array ages to the function called Sample.

6.  void Sample( int age );

7.  The word const in front of the identifier prevents the body of the function from altering or changing the contents within the function.

Section 10.7

1.  b.  cString

2.  c.  16

3.  b.  char name[] = “Sally”;

c. char name[6] = “Sally”;

d. char name[] = {’S’, ‘a’, ‘l’, ‘l’, ‘y’, ‘’};

4.  a.  true

b.  true

c.  false

d.  false

e.  true

5.  d.  the null character

6.  c.  e.getline

7.  a.  flush the input buffer

images

Section 10.8

1.  a.  <cstring>

2.  b.  lexical analysis

3.  b.  concatenation

4.  c.  destination

images

Section 10.10

1.  a.  false

b.  true

c.  true

d.  false

2.  a.  row / column

3.  b.  3

images

images

Section 10.11

1.  Webby, 21, Male

2.  True

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.75.217