Search

6.x — Chapter 6 comprehensive quiz

Words of encouragement

Congratulations on reaching the end of the longest chapter in the tutorials! Unless you have previous programming experience, this chapter was probably the most challenging one so far. If you made it this far, you’re doing great!

The good news is that the next chapter is easy in comparison. And in the chapter beyond that, we reach the heart of the tutorials: Object-oriented programming!

Chapter summary

Arrays allow us to store and access many variables of the same type through a single identifier. Array elements can be accessed using the subscript operator ([]). Be careful not to index an array out of the array’s range. Arrays can be initialized using an initializer list or uniform initialization (in C++11).

Fixed arrays must have a length that is set at compile time. Fixed arrays will usually decay into a pointer when evaluated or passed to a function.

Loops can be used to iterate through an array. Beware of off-by-one errors, so you don’t iterate off the end of your array. For-each loops are useful when the array hasn’t decayed into a pointer.

Arrays can be made multidimensional by using multiple indices.

Arrays can be used to do C-style strings. You should generally avoid these and use std::string instead.

Pointers are variables that store the memory address of (point at) another variable. The address-of operator (&) can be used to get the address of a variable. The dereference operator (*) can be used to get the value that a pointer points at.

A null pointer is a pointer that is not pointing at anything. Pointers can be made null by initializing or assigning the value 0 (or in C++11, nullptr) to them. Avoid the NULL macro. Dereferencing a null pointer can cause bad things to happen. Deleting a null pointer is okay (it doesn’t do anything).

A pointer to an array doesn’t know how large the array they are pointing to is. This means sizeof() and for-each loops won’t work.

The new and delete operators can be used to dynamically allocate memory for a pointer variable or array. Although it’s unlikely to happen, operator new can fail if the operating system runs out of memory, so make sure to check whether new returned a null pointer.

Make sure to use the array delete (delete[]) when deleting an array. Pointers pointing to deallocated memory are called dangling pointers. Dereferencing a dangling pointer can cause bad things to happen.

Failing to delete dynamically allocated memory can result in memory leaks when the last pointer to that memory goes out of scope.

Normal variables are allocated from limited memory called the stack. Dynamically allocated variables are allocated from a general pool of memory called the heap.

A pointer to a const value treats the value it is pointing to as const.

A const pointer is a pointer whose value can not be changed after initialization.

A reference is an alias to another variable. References are declared using an ampersand, but this does not mean address-of in this context. References are implicitly const -- they must be initialized with a value, and a new value can not be assigned to them. References can be used to prevent copies from being made when passing data to or from a function.

The member selection operator (->) can be used to select a member from a pointer to a struct. It combines both a dereference and normal member access (.).

Void pointers are pointers that can point to any type of data. They can not be dereferenced directly. You can use static_cast to convert them back to their original pointer type. It’s up to you to remember what type they originally were.

Pointers to pointers allow us to create a pointer that points to another pointer.

std::array provides all of the functionality of C++ built-in arrays (and more) in a form that won’t decay into a pointer. These should generally be preferred over built-in fixed arrays.

std::vector provides dynamic array functionality that handles its own memory management and remember their size. These should generally be favored over built-in dynamic arrays.

Quiz time

1) Pretend you’re writing a game where the player can hold 3 types of items: health potions, torches, and arrows. Create an enum to identify the different types of items, and a fixed array to store the number of each item the player is carrying (use built-in fixed arrays, not std::array). The player should start with 2 health potions, 5 torches, and 10 arrows. Write a function called countTotalItems() that returns how many items the player has in total. Have your main() function print the output of countTotalItems().

Show Solution

2) Write the following program: Create a struct that holds a student’s name and grade (on a scale of 0-100). Ask the user how many students they want to enter. Dynamically allocate an array to hold all of the students. Then prompt the user for each name and grade. Once the user has entered all the names and grades, sort the list by grade (highest first). Then print all the names and grades in sorted order.

For the following input:

Joe
82
Terry
73
Ralph
4
Alex
94
Mark
88

The output should look like this:

Alex got a grade of 94
Mark got a grade of 88
Joe got a grade of 82
Terry got a grade of 73
Ralph got a grade of 4

Hint: You can modify the selection sort algorithm from lesson 6.4 -- Sorting an array using selection sort to sort your dynamic array.

Show Solution

3) Write your own function to swap the value of two integer variables. Write a main() function to test it.

Hint: Use reference parameters

Show Solution

4) Write a function to print a C-style string character by character. Use a pointer to step through each character of the string and print that character. Stop when you hit a null terminator. Write a main function that tests the function with the string literal “Hello, world!”.

Hint: Use operator++ to advance the pointer to the next character

Show Solution

5) What’s wrong with each of these snippets, and how would you fix it?

a)

Show Solution

b)

Show Solution

c)

Show Solution

d)

Show Solution

e)

Show Solution

6) Let’s pretend we’re writing a card game.

6a) A deck of cards has 52 unique cards (13 card ranks of 4 suits). Create enumerations for the card ranks (2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace) and suits (clubs, diamonds, hearts, spades).

Show Solution

6b) Each card will be represented by a struct named Card that contains a rank and a suit. Create the struct.

Show Solution

6c) Create a printCard() function that takes a const Card reference as a parameter and prints the card rank and value as a 2-letter code (e.g. the jack of spades would print as JS).

Show Solution

6d) A deck of cards has 52 cards. Create an array (using std::array) to represent the deck of cards, and initialize it with one of each card.

Hint: Use static_cast if you need to convert an integer into an enumerated type.

Show Solution

6e) Write a function named printDeck() that takes the deck as a const reference parameter and prints the values in the deck. Use a for-each loop.

Show Solution

6f) Write a swapCard function that takes two Cards and swaps their values.

Show Solution

6g) Write a function to shuffle the deck of cards called shuffleDeck(). To do this, use a for loop to step through each element of your array. Pick a random number between 1 and 52, and call swapCard with the current card and the card picked at random. Update your main function to shuffle the deck and print out the shuffled deck.

Hint: Review lesson 5.9 -- Random number generation for help with random numbers.
Hint: Don’t forget to call srand() at the top of your main function.
Hint: If you’re using Visual Studio, don’t forget to call rand() once before using rand.

Show Solution

6h) Write a function named getCardValue() that returns the value of a Card (e.g. a 2 is worth 2, a ten, jack, queen, or king is worth 10. Assume an Ace is worth 11).

Show Solution

7) Alright, challenge time! Let’s write a simplified version of Blackjack. If you’re not already familiar with Blackjack, the Wikipedia article for Blackjack has a summary.

Here are the rules for our version of Blackjack:
* The dealer gets one card to start (in real life, the dealer gets two, but one is face down so it doesn’t matter at this point).
* The player gets two cards to start.
* The player goes first.
* A player can repeatedly “hit” or “stand”.
* If the player “stands”, their turn is over, and their score is calculated based on the cards they have been dealt.
* If the player “hits”, they get another card and the value of that card is added to their total score.
* An ace normally counts as a 1 or an 11 (whichever is better for the total score). For simplicity, we’ll count it as an 11 here.
* If the player goes over a score of 21, they bust and lose immediately.
* The dealer goes after the player.
* The dealer repeatedly draws until they reach a score of 17 or more, at which point they stand.
* If the dealer goes over a score of 21, they bust and the player wins immediately.
* Otherwise, if the player has a higher score than the dealer, the player wins. Otherwise, the player loses (we’ll consider ties as dealer wins for simplicity).

In our simplified version of Blackjack, we’re not going to keep track of which specific cards the player and the dealer have been dealt. We’ll only track the sum of the values of the cards they have been dealt for the player and dealer. This keeps things simpler.

Start with the code you wrote in quiz #6. Create a function named playBlackjack() that returns true if the player wins, and false if they lose. This function should:
* Accept a shuffled deck of cards as a parameter.
* Initialize a pointer to the first Card named cardPtr. This will be used to deal out cards from the deck (see the hint below).
* Create two integers to hold the player’s and dealer’s total score so far.
* Implement Blackjack as defined above.

Hint: The easiest way to deal cards from the deck is to keep a pointer to the next card in the deck that will be dealt out. Whenever we need to deal a card, we get the value of the current card, and then advance the pointer to point at the next card. This can be done in one operation:

This returns the current card’s value (which can then be added to the player or dealer’s total), and advances cardPtr to the next card.

Also write a main() function that plays a single game of Blackjack.

Show Solution

6h) Extra credit: Critical thinking time: Describe how you could modify the above program to handle the case where aces can be equal to 1 or 11.

Hint: It’s important to note that we’re only keeping track of the sum of the cards, not which specific cards the user has.

Show Solution

6i) In actual blackjack, if the player and dealer have the same score, the result is a tie and neither wins. Describe how you’d modify the above program to account for this.

Show Solution

7.1 -- Function parameters and arguments
Index
6.16 -- An introduction to std::vector

242 comments to 6.x — Chapter 6 comprehensive quiz

  • Satwant

    Quiz 2, in solution line no. 22.

            // Look for largest element remaining in the array (starting at startIndex+1).  // WRONG
            
                   // Look for largest element in the remaining array( starting at start index+1).  //RIGHT

  • Jazz

    Hi Alex,

    I want to share a beauty with you, with the next swap function in #3 quiz:

    Many thanks to you, Alex!

    • Alex

      This is a neat little integer trick that sometimes gets asked in programming interviews.

      Please don’t ever do this in real code, as it’s not obvious what the code is doing. 🙂

      • Jazz

        I’m agree with you, it’s realy looks like "magic literals". But, what about algorithms, that are not obvious too?
        This function returns Greatest Common Divisor, and it doesn’t look simple.

        Or i’m talking about different things?

        • Alex

          Good question and food for thought. I think the difference is this:

          * There’s a good alternative for the swap() function that’s not only more intuitive, but also works for non-integer types (use a temp variable, or std::swap). The algorithm presented above is “clever” (which makes it an interesting interview question), but clever isn’t a good qualification for an algorithm.

          * There isn’t really a good alternative for the GCD algorithm that’s going to be more intuitive, and GCD is only meaningful in the context of integers anyway. This is a standard way of calculating GCD. I think in this case, a good comment will go a long way.

  • Sandro

    Hello. For the #7 quiz, I wrote the program below. It works, but if you play it several times (the game allows you to keep playing), the variables starts to get weird numbers, like 20030006113. Can someone explain me why?

  • Rob G.

    Hi Alex,

    In general, I am using fewer raw pointers and more dereferencing subscripts for readability, etc. For exercise 4,
    is there some significance to why you chose to use pointers in this case? Can u plz clarify when to use pointers versus dereferencing subscripts?

    of course the pointer value and the array value are the same.

    • Alex

      I used a pointer because the quiz question said to use one. 🙂

      Generally speaking, subscripting is favored because it’s easier to use and does an implicit dereference. Pointer arithmetic may lend itself to a more elegant solution in rare cases (e.g. copying a C-style string), but it hardly matters.

  • Aymen

    Hi Alex, really frustrated with a problem I’v been having and done multiple searches online to see if any one else has been having this problem and it appears there problem isnt directly similar to that of mine.

    For the code

    I have been getting the error:

    error: aggregate ‘std::array<Card,52u> deck’ has incomplete type and cannot be defined

    I think that this may be an error with my compiler as I have done basically the same to your solution and just to ensure that its my compilers fault, I have started a new file and copied each of your solutions from the Task6 and beyond up to 6d and got the same problem.

    Any help would be strongly appreciated.
    Cheers

    • Alex

      I’m not sure if this is your problem, but you can get this problem when the compiler can see that there is a struct or class named Card (though a class forward declaration) but can’t find the implementation.

      Make sure the definition of struct Card is above your main function, where you declare the std::array of Cards.

      • Aymen

        I did Alex, but me being an idiot as usual forgot:

        So yeah, It took me a day to figure out where I’d gone wrong, I put it in at the start and then changed it to vector for some reason or another. Anyways thanks for the help and hopefully no one else will make this mistake reading!

  • Sandro

    About the quiz #2.

    In your solution you pass the array to the sortNames function. I imagine that it decays to a pointer. So, why didn’t you need to use the member selection operator (->)? It worked like a normal array to me.

    Another question. I don’t know why, but my program works without the utility operator. I tested the solution too. Can someone tell me why?

    • Sandro

      I also tried on an online compiler. It worked as well.

    • Alex

      I don’t need to use operator-> because the subscript operator[] does a dereference. That is, students[currentIndex] returns a value (of type Student), not a pointer to a value. So we use . instead of ->.

      By “utility operator”, I presume you meant utility header. It’s possible that your iostream or string libraries are including utility themselves, so when you include them, utility comes along for the ride. But you should not rely on this.

  • Rob G.

    Hi Alex I think I over thought question 2. Can you please clarify how you kept the names and grades paired? I’ve missed a simpler step and would like to know what that was.I solved the problem (below) using an array of structs. I would sort the structs based on the grades, and since the names were in the same struct, I didn’t have to worry about keeping them paired. But of course dealing with an array of structs was very educational so it was worth the effort.

    • Alex

      It looks like you basically did the same thing I did, just with more modularization. I also used an array of structs, sorted by grade. Although we’re sorting by grade, we’re swapping at the struct level, so we’re swapping the name and the grade together.

  • Arda

    Hey Alex, I found a mistake in your article. In quiz 1 you said, "Player should start with 20 arrows." but in the program you wrote, you gave the player 10 arrows.

    Have a nice day! 🙂

  • Ran

    This is a test for my new portrait!

  • Ran

    Hi~Alex:

    How to change my head portrait in your comment area?

  • Thomas

    Great tutorial, Alex. I’m at my final semester in computer engineering, had two courses of programming, basic & object-oriented, both in c++.
    This site’s explanations are of much higher quality (imo) and easily replaced my lecturer and his lectures.

    Good job!

  • Rob G.

    Alex you have a great site. Have learned so much from it. I noticed in quiz#3 that I can only pass "magical" numbers as an argument to the functions parameters so long as the parameters are not referenced (minus the ampersand). With symbolic language there is no problem.

    error: ..srcmain.cpp:20:17: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’

    • Alex

      Yes, because a and b are non-const reference variables, which require the arguments to be an lvalue (have a memory address). Literals (such as 5) don’t have an address. What does it mean to swap(3, 1)? 3 has the value 3, and 1 has the value 1. You can’t make 3 have the value 1 or vice versa.

      If you remove the ampersands, then a becomes a copy of the first argument, and b becomes a copy of the second argument. The function then swaps these copies. Finally, when the function ends, the copies are destroyed. The original arguments are left alone. So while it compiles, it’s pretty useless, because swap (a, b) would swap a copy of a with a copy of b, and the original a and b would still have their original values.

  • Rob G.

    For #2: Alex am I correct that there is no way to put

    into
    any place other than main() because of scoping issues? I am pushing hard on compartmentalization.

    Also your solution uses [] for array indexing/sorting operations, etc. I coded mine using pointers - is one preferred more than the other?

    • Alex

      Yes, main() is the best place for my_array in this case. You can maintain compartmentalization via use of functions, which you have.
      Array subscripting is almost always used for directly indexing an array, because it’s easier to read, and you’re less likely to make a mistake. Pointer arithmetic is usually only used when you have a pointer that you’re stepping through an array.

  • Ran

    Problem?

    Why the quiz 2 is the same with Chapter 6.9a’s quiz?

    • Alex

      I’m not sure how that happened. I’ve updated the question to something slightly more complex, but that should be doable if you successfully completed the quiz question for chapter 6.9a.

  • Marc

    Really amazing quiz.  Ty Alex.

    Silly question but why is the array declared to 52.  Shouldn’t it be to 51 since 0 counts too?

  • Nortski

    Excellent tutorials!

    I just need to get my head around how to structure a program, calling functions within functions blows my mind!

    I have a question regarding quiz number 7. Why do we pass a pointer into getCardValue()?

    • Alex

      > Why do we pass a pointer into getCardValue()?

      We don’t. We’re dereference cardPtr, which gives us a Card object. This Card object is passed into getCardValue().

  • 6h solution has an error: there’s no default return value.
    Generally compilers will complain and will not let such code compile. However if they do, this is still a mistake since if somehow card.rank is set to MAX_RANKS or an unhandled case, and function might return garbage, or the program may be as well terminated.
    It might be a good idea to add return 0 or -1 to either default case, or end of a function.

  • Baubas

    Hi Alex,

    quiz 2:
    Why can’t I use
    std::getline(std::cin, arrayNames[iii]);
    instead of
    std::cin >> arrayNames[iii];

    If I use the first line I got this:
    How many names: 3
    Enter name nr.1: Enter name nr. 2: aaa
    Enter name nr.3: bbb

    I don’t have the possibility to enter the first name.

  • phyer

    so far, the best tutorial I have ever seen.
    I love these quiz.
    keep up the good works.
    thank you so much.

  • Greg

    Hello Alex!

    First and foremost, thanks for the tutorials because they are quite awesome!

    As for my problem, the for loop in my void shuffleDeck() function just doesn’t seem to work properly. It only runs once so it swaps the first card with a random card then the rest of the deck is left untouched. If I go through the loop in the debugger it works as it should(I went throught the whole for cycle) but I couldn’t figure out why it fails doing so after I compile it.

    Thanks,

    Greg

    P.S.: I disincluded the enum classes, the struct and the printCard() function because the code was twice as long with them.

    • Alex

      First off, you are picking a random number between 1 and 52, but your deck array’s indices go from 0 to 51. That’s a bug, but not the reason your program isn’t working as expected.
      Second, you’re instantiating a mersenne random number generator to initialize your built-in random number generator with. That’s weird. Either use mersenne or rand(), but not both.
      Third, you should only instantiate your random number generator (and/or use srand()) once, not every time the function is called. I suspect this is why your randomness is failing.

      When I deleted the first two lines of your randomGenerator() and added the following line to the top of main():

      (and fixed the array indices issue), everything worked fine.

  • Sean Kelly

    Thank you for this great comprehensive quiz!

    It makes me happy on the amount of information I have retained, and gave a chance to really screw around with what we learned.

  • Mike

    Hi Alex!

    In the solution for quiz #2 you use std::swap(), but do not have <algorithm> nor <utility> included.

    In quiz #6a problem description there’s a missed comma between diamonds and hearts.

    Problem description in quiz #6h was confusing because I didn’t know the rules of the game except for what you’ve explained in quiz #7. My first thoughts were that the players decide to count an ace either 1 or 11 (throughout the game) before they start playing, or that each time you see an ace you should determine whether it’s 1 or 11 according to some randomness, which doesn’t really make any sense. I had to read the rules elsewhere to realize that you can _choose_ to count an ace 1 or 11 (whichever is better for your score). Maybe a bit more detailed explanation would help the unfamiliar with the game.

    Thank you for such a great tutorial!

    • Alex

      Thanks for the feedback. I’ve fixed the syntax issues and clarified the rules around how aces are meant to be handled, and added a link to a summary of the game for those who are unfamiliar with it.

  • Lukas

    why can’t I use vector as dynamic array here?:

    Thanks!

    • Alex

      You can. The problem you’re having is that names is a vector of length 0. So when you’re using operator[] to access the array elements, you’re outside of the range of the array.

      All you need to do is add this line jusy beyond where you declare vector names:

  • Wonn Jen

    For the printDeck() function, I origially used a new variable, otherCard, and called swapCard() on both cards. But the deck it returned contained repeated cards. Would you mind explaining why is that so?
    Thanks!

    • Alex

      The goal is to swap two cards, right? So the code I wrote picks a random index, and then swaps the values of deck[i] and deck[otherIndex]. No duplicates introduced.

      Now look at your code:

      The first thing that happens is otherCard becomes a copy of deck[getRandomNumber(0,51)]. So when you do your swapCard, you’re swapping deck[i] with the copy of the value of deck[getRandomNumber(0,51)] instead of the actual random card picked. After your code runs, deck[i] will hold the value of the random card, but the random card never got assigned the value of deck[i] (the copy did instead).

      You could fix this like this”

      Now when you swap deck[i] and otherCard, you’re swapping deck[i] into the actual random card rather than a copy of it.

      Make sense?

  • Alexandre

    Hi Alex ! Thank for the series. Great work!

    One question…

    At Pointer to Deck, in your solution, you used

    This produced very predictable drawns.(almost always the same)
    I changed it to

    This gives a correct randomness.

    Did i have missed something here ?

    • Alex

      This doesn’t make sense to me. All cardPtr is doing is iterating through the previously shuffled card array. Switching from post-increment to pre-increment should only have the impact of skipping the first card in the deck, not anything to do with how random the results are.

      • Alexandre

        You are right! Maybe i need to take more breaks between the chapters!
        Today i checked the code again and found the problem.
        It was very subtle!

        Thank you again.

  • Jaq

    For solution 2 if you enter a letter instead of a number for the first prompt the do while loop never ends. I seem to recall a way in which you could avoid this, did you leave this out to simplify the code for the example?

  • Gopal

    Quiz#5 and question#e: You also missed return 0; at end of main function.

  • Bene

    Hi Alex, thank you very much for your great tutorials!

    I have a question regarding quiz 4): What exactly is the pointer

    declared in line 8 of your solution needed for?
    Wouldn’t the program behave exactly the same if we used the pointer passed to the function?
    Thanks, Bene 🙂

  • Josh

    Thank you so much for adding these quiz questions, they are very helpful.

  • Avneet

    Closing [/solution] tag is missing in 5e. I will enjoy solving these 🙂

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter