7.x — Chapter 7 comprehensive quiz

Chapter summary

Another chapter down! The next chapter is the best one, and you’re almost there! There’s just this pesky quiz to get past…

Function arguments can be passed by value, reference or address. Use pass by value for fundamental data types and enumerators. Use pass by reference for structs, classes, or when you need the function to modify an argument. Use pass by address for passing pointers or built-in arrays. Make your pass by reference and address parameters const whenever possible.

Values can be returned by value, reference, or address. Most of time, return by value is fine, however return by reference or address can be useful when working with dynamically allocated data, structs, or classes. If returning by reference or address, remember to make sure you’re not returning something that will go out of scope.

Inline functions allow you to request that the compiler replace your function call with the function code. You should not need to use the inline keyword because the compiler will generally determine this for you.

Function overloading allows us to create multiple functions with the same name, so long as each function is distinct in the number or types of parameters. The return value is not considered when determining whether an overload is distinct.

A default argument is a default value provided for a function parameter. If the caller doesn’t explicitly pass in an argument for a parameter with a default value, the default value will be used. You can have multiple parameters with default values. All parameters with default values must be to the right of non-default parameters. A parameter can only be defaulted in one location. Generally it is better to do this in the forward declaration. If there are no forward declarations, this can be done on the function definition.

Function pointers allow us to pass a function to another function. This can be useful to allow the caller to customize the behavior of a function, such as the way a list gets sorted.

Dynamic memory is allocated on the heap.

The call stack keeps track of all of the active functions (those that have been called but have not yet terminated) from the start of the program to the current point of execution. Local variables are allocated on the stack. The stack has a limited size. std::vector can be used to implement stack-like behavior.

A recursive function is a function that calls itself. All recursive functions need a termination condition.

A syntax error occurs when you write a statement that is not valid according to the grammar of the C++ language. The compiler will catch these. A semantic error occurs when a statement is syntactically valid, but does not do what the programmer intended. Two common semantic errors are logic errors, and violated assumptions. The assert statement can be used to detect violated assumptions, but has the downside of terminating your program immediately if the assertion statement is false.

Command line arguments allow users or other programs to pass data into our program at startup. Command line arguments are always C-style strings, and have to be converted to numbers if numeric values are desired.

Ellipsis allow you to pass a variable number of arguments to a function. However, ellipsis arguments suspend type checking, and do not know how many arguments were passed. It is up to the program to keep track of these details.

Quiz time!

1) Write function prototypes for the following cases. Use const if/when necessary.

a) A function named max() that takes two doubles and returns the larger of the two.

Show Solution

b) A function named swap() that swaps two integers.

Show Solution

c) A function named getLargestElement() that takes a dynamically allocated array of integers and returns the largest number in such a way that the caller can change the value of the element returned (don’t forget the length parameter).

Show Solution

2) What’s wrong with these programs?


Show Solution


Show Solution


Show Solution


Show Solution


Show Solution

3) The best algorithm for determining whether a value exists in a sorted array is called binary search.

Binary search works as follows:

  • Look at the center element of the array (if the array has an even number of elements, round down).
  • If the center element is greater than the target element, discard the top half of the array (or recurse on the bottom half)
  • If the center element is less than the target element, discard the bottom half of the array (or recurse on the top half).
  • If the center element equals the target element, return the index of the center element.
  • If you discard the entire array without finding the target element, return a sentinel that represents “not found” (in this case, we’ll use -1, since it’s an invalid array index).

Because we can throw out half of the array with each iteration, this algorithm is very fast. Even with an array of a million elements, it only takes at most 20 iterations to determine whether a value exists in the array or not! However, it only works on sorted arrays.

Modifying an array (e.g. discarding half the elements in an array) is expensive, so typically we do not modify the array. Instead, we use two integer (min and max) to hold the indices of the minimum and maximum elements of the array that we’re interested in examining.

Let’s look at a sample of how this algorithm works, given an array { 3, 6, 7, 9, 12, 15, 18, 21, 24 }, and a target value of 7. At first, min = 0, max = 8, because we’re searching the whole array (the array is length 9, so the index of the last element is 8).

  • Pass 1) We calculate the midpoint of min (0) and max (8), which is 4. Element #4 has value 12, which is larger than our target value. Because the array is sorted, we know that all elements with index equal to or greater than the midpoint (4) must be too large. So we leave min alone, and set max to 3.
  • Pass 2) We calculate the midpoint of min (0) and max (3), which is 1. Element #1 has value 6, which is smaller than our target value. Because the array is sorted, we know that all elements with index equal to or lesser than the midpoint (1) must be too small. So we set min to 2, and leave max alone.
  • Pass 3) We calculate the midpoint of min (2) and max (3), which is 2. Element #2 has value 7, which is our target value. So we return 2.

Given the following code:

3a) Write an iterative version of the binarySearch function.

Hint: You can safely say the target element doesn’t exist when the min index is greater than the max index.

Show Solution

3b) Write a recursive version of the binarySearch function.

Show Solution

8.1 -- Welcome to object-oriented programming
7.14 -- Ellipsis (and why to avoid them)

165 comments to 7.x — Chapter 7 comprehensive quiz

  • Avijit Pandey

    Hi! Here is my attempt to the recursive version for the 3rd problem.

    I wanted to ask why you chose not to include the last condition(that the array can no longer be halved) as the base case.
    Also, please point out any mistakes that I'm not noticing.

    • - Initialize your variables with brace initializers.
      - Inconsistent formatting. Use your editor's auto-formatting feature.
      - Line 16 is always true. The way your code is now, `binarySearch` is missing a `return`-statement.
      - Line 10 could be `min >= max`. If `min == max`, then the array segment's length is 1 and you know that this last element is not the target because of line 8.

      Checking equality at the beginning is wasteful. It's unlikely that this check is true as long as the array segment's length is not 1. It's much more likely that the target is to the left or right of the mid point, so those checks should run first.

  • Anastasia

    I'm not sure whether it belongs here (sorry if it doesn't), but I wanted to put binary search in a context in order to see better how it works. So I made it play hi-lo game we've coded in chapter 5 quiz (it wins always).

    Line 69 (`int guess { ... };`) is ridiculous, is there a better way to handle/avoid those casts?

    • Hi!

      > Line 69 (`int guess { ... };`) is ridiculous, is there a better way to handle/avoid those casts?
      You don't need the cast in line 70. Your vector is a vector of int, you're casting int to int. You only need the cast for the index.

      Your entire vector is not needed. It's a contiguous list of numbers, `values[i] = (i + 1)`, you can calculate that on the fly.

      It's still a binary search if you do this, but with an imaginary list.

      • Anastasia

        > You don't need the cast in line 70. Your vector is a vector of int, you're casting int to int. You only need the cast for the index.
        I could swear my compiler kept complaining about types untill I did what I did with line 69. You're right though, I see it now.

        > Your entire vector is not needed.
        But what if I want to make it guess from a different set of numbers (say from 150 to 500)? The way I did it, I'd only need to change MIN_VALUE and MAX_VALUE respectively.

  • A

    I've checked my code against the solution several times, and can't seem to find anything wrong, yet each time I run it, it states each test value passed but the last. Am I missing something?

    • Change line 35 to

      to see what your `binarySearch` is saying. Compare the result to what you expected (Manually search 49 in the array) and what your `expectedValues` says it should be.

  • mmp52

    Hey, this might seem stupid but why didn't he use const double's when defining max Function? Aren't we supposed to use const everywhere if we don't manipulate the input?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]