6.x — Chapter P.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.

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. Range-based for-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_view and 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 nullptr (before in C++11, 0) 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 it is pointing to is. This means sizeof() and range-based for-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. If you’re writing software for a memory-limited system, make sure to check if new was successful.

Make sure to use the array delete (delete[]) when deleting an array. Pointers pointing to deallocated memory are called dangling pointers. Using the wrong delete, or dereferencing a dangling pointer causes undefined behavior.

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, handles its own memory management and remembers its size. These should generally be favored over built-in dynamic arrays.

Thanks to iterators, we don’t have to know how a container is implemented to loop through its elements.

The algorithms library helps us to save a lot of time by providing many off-the-shelf functions. In combination with iterators (and later lambdas), the algorithms library is an important part of C++.

Quiz time

To make the quizzes a little easier, we have to introduce a couple of new algorithms.

std::reduce applies a function, by default the + operator, to all elements in a list, resulting in a single value. When we use the + operator, the result is the sum of all elements in the list. Note that there’s also std::accumulate. std::accumulate cannot be parallelized, because it applies the function left-to-right. std::reduce segments the list, which means that the function is applied in an unknown order, allowing the operation to be parallelized. If we want to sum up a list, we don’t care about the order and we use std::reduce.

Author's note

std::reduce is currently not fully implemented in all major standard libraries. If it doesn’t work for you, fall back to std::accumulate.

std::shuffle takes a list and randomly re-orders its elements.

Possible output

2 1 4 3

Question #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 an std::array to store the number of each item the player is carrying (The enumerators are used as indexes of the 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() as well as the number of torches.

Show Solution

Question #2

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

For the following input:


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

You can assume that names don’t contain spaces and that that input extraction doesn’t fail.

Show Solution

Question #3

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

Show Hint

Show Solution

Question #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 the null terminator. Write a main function that tests the function with the string literal “Hello, world!”.

Show Hint

Show Solution

Question #5

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


Show Solution


Show Solution


Show Solution


Show Solution


Show Solution

Question #6

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

a) 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). Those enumerators will not be used to index arrays.

Show Solution

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

Show Solution

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

Show Hint

Show Solution

d) 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. Do this in a function named createDeck and call createDeck from main. createDeck should return the deck to main.

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

Show Solution

e) Write a function named printDeck() that takes the deck as a const reference parameter and prints the cards in the deck. Use a range-based for-loop. When you can printDeck with the deck you generated in the previous task, the output should be

2C 3C 4C 5C 6C 7C 8C 9C TC JC QC KC AC 2D 3D 4D 5D 6D 7D 8D 9D TD JD QD KD AD 2H 3H 4H 5H 6H 7H 8H 9H TH JH QH KH AH 2S 3S 4S 5S 6S 7S 8S 9S TS JS QS KS AS

If you used different characters, that’s fine too.

Show Solution

f) Write a function named shuffleDeck to shuffle the deck of cards using std::shuffle. Update your main function to shuffle the deck and print out the shuffled deck.

Reminder: Only seed your random number generator once.

Show Solution

g) 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

Question #7

a) 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(). This function should:

  • Accept a shuffled deck of cards as a parameter.
  • Implement Blackjack as defined above.
  • Returns true if the player won, and false if they lost.

Also write a main() function to play a single game of Blackjack.

Show Solution

b) 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.

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

c) In actual blackjack, if the player and dealer have the same score (and the player has not gone bust), 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
6.18 -- Introduction to standard library algorithms

968 comments to 6.x — Chapter P.6 comprehensive quiz

  • Max Morgan

    Hi Alex and Nascar,

    Do all of the Struct variables in the deck have the same name, card? I understand that we set card to size_type so that we can use it for subscripting and it is set to zero so that we can index the first element in the array Deck. My question is, does index_t card = 0; also simultaneously declare the first struct variable? Does ++card then create a new struct variable every time the loop goes around?

    Hopefully this is not a horrendously dumb question and I would greatly appreciate input from anyone who can give me some guidance here.

  • Barne

    I have a question about your function shuffleDeck (solution for question 6g).

    Specifically about the swapIndex variable. I thought we used index_t to avoid potential unsigned/signed mismatch. But since the function getRandomNumber is of type int, won't it just stick an unsigned integer into the variable by default? Shouldn't it be something like:

    Or am I completely misunderstanding index_t?

    • It should be

      • Barne

        I swear to god @nascardriver.

        All the times I check the posts you are telling someone to use uniform initialisation, you got me double, triple checking my code for it even when I never intend to post it anywhere. Guess I finally got hit by it myself!

        Thank you for your guidance and help to everyone on this website!

  • Loved this quiz.
    Mine :-

  • There's another solution to quiz 5c.

    To pass the array by reference.

  • Steve S

    Hi - loving the tutorials. I'm trying to use mersenne to shuffle the deck but it only ever shuffles the first card - the rest is unshuffled. Relevant code below - any help appreciated!

    • Seed random number generators only once.
      You're seeding @mersenne every time @getRandomNumber is called.
      You're seeding it with the same seed (The current time in seconds, which doesn't change between calls, because your code doesn't take a second to run) every time, so you'll get the same numbers every time.

      @Alex This is a common mistake, please mention it in the relevant lesson (I said this before, but in a long comment where it's easy to miss)

      • Alex

        I thought I had, but clearly not visibly enough. Lesson 5.9 has been amended to address the common cases that users of random number generators tend to run into, including this one. I also added a reminder to the quiz question to only seed your random number generator once.

  • Arno Adam

    I'd like my solution looked over as well. I opted not to show the score to the player, bacause they have to calculate it themselves in real life blackjack as well. Instead I used a std::vector to list them the cards in their hands. That way, I have a good use for the printCard() function as well.

    My solution for a draw in the game involves a loop in the playBlackjack() function so that the game simply continues with the same deck and new hands. I realize that this has the potential for the deck array to go out of scope, but I figured the chance for successive draws until all cards are exhausted would be pretty slim.

    And, yes. I know my shuffleDeck() function is overkill, but I wanted to see if I could use modulus correctly :D

    • Hi Aron!

      * Line 82: Initialize your variables with uniform initialization. You used copy initialization.
      * Line 88: Initialize your variables with uniform initialization. You used direct initialization.
      * Line 34, 35, 90, 119, 137: Initialize your variables with uniform initialization.
      * Line 73, 90, 220: Limit your lines to 80 characters in length for better readability on small displays.
      * Use enum class instead of enum
      * Use ++prefix unless you need postfix++. Same for --
      * Seed random number generators only once. @mersenne is seeded every time @shuffleDeck is called (@Alex People do this often, it might be worth a comment in the relevant lesson).
      * Don't pass random numbers to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max()

      > the deck array to go out of scope
      It won't go out of scope. @cardPtr will reach it's end.

      > but I figured the chance for successive draws until all cards are exhausted would be pretty slim
      Don't do that. Your program should be stable under all circumstances. Even if all you have is an @assert, that's still better than undefined behavior. Missed bound checks are the cause for many exploits.

      • Tyson

        Can I avoid seeding the random number generator more than once by using the static keyword?
        if I can't, then how can I seed it only once??

        • * Line 10, 14: Initialize your variables with uniform initialization. You used copy initialization.
          * Line 9: Limit your lines to 80 characters in length for better readability on small displays.

          Static is the correct choice.

  • Louis Cloete

    @Alex, at Quiz 6h), isn't it better to use deliberate switch fall-through to avoid copying the "return 10;" statement four times? I.e.:

    instead of

    • Alex

      Generally speaking, yes, it's better to not repeat yourself. However, for such a simple return values, and given the way the switch is formatted (case on the left, return value on the right), I find it easier to understand with the repeated values than // fallthough comments for RANK_10, RANK_JACK, and RANK_QUEEN.

  • Jon

    Hi! Here's my solution for the Blackjack game with aces-as-1-or-11s included. I wanted to get as close as possible to the flow of real blackjack so I added the extra variables playerTotalWithAces and DealerTotalWithAces (probably could have named them more accurately in hindsight) so I could output different, more realistic text throughout the game depending on how many aces were in each hand. My dealer also must hit on Soft 17's, which turned out to be a little tricky to implement but I think I got it! Hopefully it makes sense, I'd love any suggestions/helpful hints, thanks so much!

    • Hi Jon!

      * Line 100, 101: Initialize your variables with uniform initialization. You used direct initialization.
      * Line 43, 44, 223, 320: Initialize your variables with uniform initialization.
      * Line 121, 147, 156, 186, 202, 205, 230, 233, 236, 239, 251, 255, 258, 259, 261, 264, 267, 277, 278, 280, 283, 286, 289, 292, 307, 308, 341, 364: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 119, 325: Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().
      * Magic numbers: 10, 11, 17, 21, 22, 52
      * @drawPlayerCard and @drawDealerCard are duplicates
      * Line 340ff: Use curly brackets if your statement exceeds 1 line

  • Thao Nguyen

    Hello :) This is my implementation of question 6g. I use Mersenne to generate random number, but I put the die and the mersenne objects in global scope, since I don't want to instantiate these objects every time I call shuffleDeck. I'm wondering if there's a better way to handle this.

    Additional comments are appreciated.

    Thanks a lot!

    • Hi!

      * Line 9: Initialize your variables with uniform initialization. You used copy initialization.
      * Line 2: Initialize your variables with uniform initialization. You used direct initialization.
      * Line 3: Limit your lines to 80 characters in length for better readability on small displays. You can move line 6 above line 3 and use @index_t here.

      > I'm wondering if there's a better way to handle this.
      Lesson 4.3 - Static duration variables

  • Jon

    Hi again! For problem #4, if I define the printString function to take a (char *ptr) argument rather than (const char *ptr) like you have in there, my compiler gives me a warning message "ISO C++11 does not allow conversion from string literal to 'char *'", though the program still seems to work fine.

    Why does adding the const make the warning go away? Thanks!

    • Hi Jon!

      String literals are immutable. If @ptr is a char*, you're allowed to modify the argument. But you're passing a string literal, which you're not allowed to modify, hence the warning/error.
      Make sure you followed lesson 0.10 and 0.11. Your code should not compile.

  • Jon

    Hi, thanks again for putting up with all my questions! For the first quiz question, we should not be using an enum class and static casting in this instance? Just wanted to ask since earlier you mentioned to use enum classes over regular enums from c++11 on.

    Also, in my original solution in the countTotalItems function I used dereferencing a pointer and pointer arithmetic, looks like it just unnecessarily complicates things?

    • Hi Jon!

      If your enum is encapsulated inside a class or namespace, you can use a regular enum. If that's not the case, you should use an enum class. I don't know why Alex used a global enum in this quiz.

      > looks like it just unnecessarily complicates things?
      It does. The subscript operator ([]) for arrays is defined as

      In the end, your code is the same as using the subscript operator. But your code is more complicated without gaining anything from it.
      If you need to sum up a list in the future, you can use @std::accumulate

      • Louis Cloete

        Alex used a regular enum in the quiz to enable directly comparing MAX_ITEMS to an int, instead of having to do this:

        see under the headings "Arrays and enums" and "Arrays and enum classes." Best solution would be to do this:

        • I'd like to take this chance to point out that operators can be overloaded for enum class types.

          This is useful if item comparison is independent of the order of declaration.
          If items compare in the order of their declaration, your solution is fine.

          • Louis Cloete

            I am not quite following what you mean by the last paragraph below the code. What does it mean to compare items in the order of declaration or not?

            • You want (a < *) to be true for all * after a.
              If that's the case, you don't need to overload the operator and can use casts or a regular enum.
              If you want (a > *) for some *, you need to overload the operator.

              One comment to your previous comment

  • Riven

    Thank you for this wonderful tutorial series!
    I really appreciate every unit!

  • Faruk

    Hi, i have written the Blackjack Game and its been very FUN and my code is very different from the result.Mostly because i used structs to represent the player and the dealer.I would very appreciate it if someone could give me some tips, code improvements and feedback.