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 first 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 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

Hint: You can modify the selection sort algorithm from lesson 6.4 -- Sorting an array using selection sort to sort your dynamic array. If you put this inside its own function, the array should be passed by address (as a pointer).

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 the ++ 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?


Show Solution


Show Solution


Show Solution


Show Solution


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.
Hint: Don’t forget that std::array’s operator[] expects an index of type size_type, and that size_type must be prefixed by the full name of the array type to be accessed.

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.
Reminder: Only seed your random number generator once.

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

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

7b) 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.16 -- An introduction to std::vector

701 comments to 6.x — Chapter 6 comprehensive quiz

  • Alexander S.

    How is this for Quiz #1?

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability.
      - Don't set enumerator values unless you use them.
      - `totalItems::player` should be `const`.
      - `LOG::player` should be `const`.
      - Line 28+: You're not using `index` for anything. Remove `cycle` and use `index`.

      Have a look at the solution to see how this can be done with a 1dimensional array.

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    This my quiz no 6 so far! What do you think?

    1. This is card.h

    2. This is cardfunctions.h

    3. This is printcard.cpp

    4. This is my insertcard.cpp

    5. This is printdeckofcard.cpp

    6. And this is my swapcard.cpp (actually this is redundant because I can use std::swap instead)

    7. This is my program

    • Hi!

      Remove `MyCard`. It's the same as `Card` but less usable. (Ok, I see what you're using it for, but that doesn't help a lot. If each card has a unique identifier, you don't need a list.)
      > The alternative
      Identifiers starting with an underscore (_) are reserved names. Don't use them.

      You're now using "MyCard" for 2 different things (Namespace vs Parameter).
      Yes, include "card.h". Your code only compiles because every file that uses "cardfunctions.h" includes "card.h" first. Your includes should be independent of their order.
      Use a global type alias for `std::array<Card, 52>`.

      Use single quotation marks for characters.

      You don't need to create a .cpp for every function. It's fine to define all functions of one header in the same .cpp file.
      The rest looks good!

      • Samira Ferdi

        Thank you very much, Nascardriver!

        So, these are my changes:

        1. card.h

        2. cardfunction.h

        3.cardfunctions.cpp (I move my functions here)

        • > Should I define card_t once again here?
          No, you already have access to it through "cardfunctions.h". Use it in line 76.

          > My program run fine if I don't include this. Should I?
          Yes. If you use something, include its header.

          • Samira Ferdi

            Thank you, Nascardriver!
            So, is like this?

            So, for cardfunctions.cpp, it's like I should #include<string> (if I use std::string) although <string> header define in <iostream>?

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    how to make empty std::array? I have a problem about it.

    The result is:
    00 00 00 00 20 00 20 00 00 0C 20 00 4C
    00 00 00 2C 00 2C 20 2C 00 00 00 20 00
    and so on and so forth...
    The point is the result is not empty(in this context I mean 00)

    This is my printDeckOfCards()

    And this is my printCard()

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    Alex, you recommended that struct should be passed by reference and built-in array should be passed by address. How I passed built-in array that has type struct?

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    This is my code for quiz no.2 (minus sorting). What do you think?

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    I wanna ask you about the compiler complain about unitialized struct member, so, I should do this instead uninitialized struct members. Is it okay?

    But, if I do this, my program runs fine and compiler throw error if I initialized the struct member. Why this happen?

    My second question is, what is the consideration about accessing struct member using reference or pointer if struct passed into function?

    My last question is, how is the way passing struct by pointer? My code below is not run

    • > Is it okay?
      Yes, initialize struct members. Line 3 should use empty curly braces.

      Post your compiler's error messages.

      • Samira Ferdi

        Hi, Nascardriver! This is for my first question above!
        This is when I got (compiler's error message) when I just initialized my struct members (this is just the only change, and all others is still the same).But, if I don't initialized my struct members, this program above runs fine (with no any errors and warnings)

  • Sam

    Solution for question #2

    • - Line 16 only has to run if extraction failed, not if `value < 1`. This isn't an expensive call, so it's not a huge deal, but shouldn't be done.
      - Avoid unsigned types.
      - Line 41: Good job, not a lot of people get this right.

      • Sam

        Thanks! This should fix it

  • Sam

    Solution question #1

  • Anastasia

    Thanks for this great quiz!
    6-all, 7, 7a, 7b
    I have some doubts and questions, mostly related to my use of namespaces and global variables.
    - I went for 3 namespaces, because I wanted to make it clear which purpose in the code each piece of data serves, but maybe it would be better to stick with 1 namespace for everything, since the code is pretty straightforward?
    - is my usage of global variables acceptable? Some of them (total_max_score and dealer_max_score (lines 52, 53)) I could have made local, but it seemed more self-explanatory this way.
    - are type aliases and structs normally put in a namespace?
    and also:
    - are values in `getCardValue()` (lines 161, 163) and `drawCard()` (line 182) considered 'magic'? Should I do something about that?

    *I hope I understood the rules of the game.

    • - Weird formatting. Use your editor's auto-formatting feature (Missing indention, enum in 1 line).
      - Use `enum class` unless you use the enum for indexing.
      - Line 132: Inside the angle brackets you can specify the type returned by the distribution. That way you don't need to cast in line 134.
      - Line 82, 98, 161: Inconsistent error handling.
      - Line 161: Should be `std::exit`.
      - Line 165: You forgot an ampersand.
      - Line 188: Should be a `switch`, because you're checking for equality of a very limited set of values.
      - Line 188, 197+: Duplicate comparison. Add an `enum class` with hit and stand enumerators. If you can think of a good function name, you can also use a `bool`.
      - Using upper camel case for types and namespaces will get confusion, because you can't tell the difference at the point of use (Both can be accessed via `name::name`. You can re-use lower case underscore for namespaces, as functions don't support `operator::`.
      - Some empty lines won't hurt your code, especially in `drawCard` and `playBlackjack`.

      Your code looks very good overall, keep up the great work!

      > 3 namespaces
      `Rank` and `Suit` can be moved into `Card`, then you can even use a regular `enum`.
      `random` should be separated from the rest, so it's fine.
      `Blackjack` would be implemented as a class, you'll learn about those later.

      > usage of global variables
      It's fine. Moving them into function makes updating your code harder. Again, you'd usually have them in a class.

      > type aliases and structs normally put in a namespace?
      In a namespace, yes, because pretty much everything should be in a namespace. In a class, most of the time. In your case, they'd be in a class/struct.

      > magic getCardValue
      No, they only appear once and right with an identifier they're related to.

      > magic drawCard
      Yes, this `10` is actually `getCardValue({ CardDeck::Rank::ACE, {} }) - 1` (This would look prettier if `getCardValue` accepted a `CardDeck::Rank`. Function overloading (Covered later) will make this easy.)

      • Anastasia

        > Weird formatting. Use your editor's auto-formatting feature
        > Some empty lines won't hurt your code, especially in `drawCard` and `playBlackjack`
        I know, sorry. I've tried a new auto-formatter, it was supposed to 'beautify' my code.

        > Line 82, 98, 161: Inconsistent error handling.
        I don't know how to handle errors yet, just trying to exit when un unexpected result would lead to bad consequences.

        > Line 188: Should be a `switch`, because you're checking for equality of a very limited set of values.
        > Line 188, 197+: Duplicate comparison. Add an `enum class` with hit and stand enumerators. If you can think of a good function name, you can also use a `bool`.
        Can I do something like that (with a better name, maybe)?

        edit: in playerTurn() while condition '!wentBust(player)' should be first to be checked, otherwise there will be an unnecessary prompt when player goes bust.

        Thank you so much for all your suggestions and responses, it's of huge help to me!

        • > I've tried a new auto-formatter
          You can probably adjust it to not remove empty lines and to indent namespaces and switches. This boils down to personal preference. I think the current formatting confiscates readability too much.

          > I don't know how to handle errors yet
          No worries, that's covered later. The issue is that you're inconsistent. One time you print a message, the other time you print a message and exit.

          > Can I do something like that
          Yes, that's what I meant. As long as you can find a function name that makes it obvious which return value means what, this solution is fine.

  • Arnima

    My apologies if this has already been asked and answered. While I technically understand the syntax of

    I'm not sure I completely understand why this is done, and then how 'card' can be used in the for statement as deck[card]...

  • Michael Hackett

    In 6e, why is "card" inside the for/each loop a reference?

  • Hey, is there a better way to go around the repetition in the PlayBlackjack function?

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability on small displays.
      - Use your editor's auto-formatting feature.
      - `PlayBlackjack` is inconsistently named.
      - Add a type alias for your deck.
      - Seed random number generators only once.
      - Magic number: 51.
      - Use --prefix unless you need postfix--.
      - Use single quotation marks for characters.

      Without line numbers, I can only assume that you mean line 93-103 and 108-120.
      Add a struct `Player` which stores all information about a player. When you want to apply the same code to both players, use a loop.

      You'll have to sacrifice line 112 and 119 or add a check inside the loop.

      • Thanks.
        How do I seed random number generators only once, I type exactly what is shown in section 5.9. These similar 3 lines

        • But you put it into a function that might get called multiple times (`shuffleDeck`). Every time you create `mersenne`, you're seeding it with the current time. If you're creating it twice in the same second, you'll get the same "random" numbers.
          Move `mersenne` out of there, eg. into a namespace an seed it from `main`. Or declare `mersenne` static.

  • Would you help me understand this. First: how do I tell the random generator not to generate a previous number. And second, even when a number has previously been generated, based on the fact that I'm swapping between array element, I wasn't expecting those values to repeat. Thanks.

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability on small displays.
      - Seed random number generators only once.
      - Use your editor's auto-formatting feature.
      - Magic numbers: 51, 52.

      > how do I tell the random generator not to generate a previous number
      You can't, it wouldn't be random anymore. Check if the number has been seen before and re-generate.

      > based on the fact that I'm swapping between array element
      You're swapping with copies, line 16.

  • Atas

    A neat alternative solution to 7b:

    Requires an extra max function and rather hard to read though.

  • Vir1oN

    Also my getCardValue() function seems to be a bit different from yours

    Can you say which is better?

  • Vir1oN

    When I tried to use a for-each loop in the shuffleDeck() function, I got an exception-error saying "Unhandled exception at 0x5492E906 (ucrtbased.dll) in Chapter 6 Final 3.exe: An invalid parameter was passed to a function that considers invalid parameters fatal." And the interesting thing is that the error doesn`t occur every single time, so it seems like something causes undefined behavior.

    Could you explain to me why this happened? The other parts of the program is similar to the snippets written in solution sections.
    As far as I know, for-each loops are undesirable in functions when we pass a pointer (which doesn`t know the array`s size), but here I used std::array instead, which doesn`t decay to a pointer. So what is the issue here?

  • Alex

    You're correct. Constexpr functions need to be written such that parameters can be compile-time or runtime values. Thus it doesn't work in this case.

  • Nenad

    So function parameters can never be compile-time constants, noted. Thank you kind sir.

  • We could do this

    `length` is not compile-time constant, because its value might be produced at run-time.
    As of now, we can't mark parameters as compile-time constant. This might change in C++20 by `consteval`.

  • Nenad

    Sorry if it's a stupid question, but I've trouble grasping compile-time constant.
    In the above example,

    why exactly is length not a compile time constant?
    I couldn't find materials online that properly explain this. Usually, an example is used where one const variable is initialized using a literal value, and another const variable is initialized using std::cin. And this isn't of much help since here it's obvious which variable is and which isn't a compile-time constant.
    But, then I come across examples such as this one and I can't tell whether a variable is compile-time or runtime.

    • Alex

      It's not a stupid question at all.

      Normal function calls are resolved at runtime, in this case with the argument being copied into the length parameter at the point where the function call executes. Because of this, length is considered a runtime parameter.

      Note that C++ does support constexpr functions, which can have parameters that are compile-time constants. Those aren't covered in these tutorials yet, but you can find some information here:

      • Correct me if I'm wrong.
        Arguments of `constexpr` functions can be `constexpr`, but we don't know that when writing the function and aren't allowed to use the arguments in a `constexpr` sense.

        It might work for `consteval`, I haven't read up on it yet and couldn't find a compatible compiler to test it.

Leave a Comment

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