Search

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 indirection 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. Indirection through 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 indirection through 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 an indirection and normal member access (.).

Void pointers are pointers that can point to any type of data. Indirection through them is not possible 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

10
10
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:

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

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?

a)

Show Solution

b)

Show Solution

c)

Show Solution

d)

Show Solution

e)

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.

If you solved this quiz, have a look at the most common mistakes as a self-test:

Show Hint

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
Index
6.18 -- Introduction to standard library algorithms

1,049 comments to 6.x — Chapter P.6 comprehensive quiz

  • yeokaiwei

    Could I request for some help?

    I'm stuck at PrintDeck.

    There are 0 compiler warnings and it will crash.

    I'm going to send this as feedback to Visual Studio 2019.

    I've written long-winded comments to let everyone know my train of thought.

    • nascardriver

      Your code doesn't compile (And it shouldn't) with clang, gcc, or msvc. I suppose you're not compiling the current version of this file.
      Even when eliminating the erroneous code, the rest of your code causes warnings, indicating that you have misconfigured your compiler or your project has been broken for a long time.

      • yeokaiwei

        It took me a while but I found the problem.

        Guess where it was?

        "void shuffleDeck(const std::array<Card, 52>& array)"

        Can you see the problem?

        It was the const.

      • yeokaiwei

        This is the 2nd version of my code.

        I've written down the Errors that I've encountered and what were the mistaken trains of thoughts.

        I finally got it to compile.

        Now, I'm trying to do playBlackjack().

        However, I'm stuck at the removing the cards from the array.

        This is not the final code.

  • yeokaiwei

    2. How do you link this card program to an Excel file that has cards with Rank and Suit? The Excel file will have non-poker cards. Each one is unique with their own Rank and Suit

    So, you don't have a deck of Rank 1 to 13.

    How would you do UNO?

    How would you do Magic the Gathering?

  • yeokaiwei

    1. Feedback. Passing an array through a function (pointer decay) is aggravating.

  • yeokaiwei

    This is for Quiz 2.

    I hope this is acceptable.

    The main issue I had was with the std::sort.

    In the std::sort algorithm, I needed to create a custom sort that returned a boolean.

    It took a lot of Google-fu jumping from one website to another.

    If you did not use the boolean, the compiler will not give you any warning and Visual Studio 2019 will just crash when you debug it.

    If there are any errors with the code, I hope you can help me point it out.

    Thank you.

    • nascardriver

      - Line 17 is always `true`
      - Line 38, 50: Use list-initialization
      - `i` doesn't exist after the for-loop it was used it. You can reuse the name "i" in the for-loop in line 50.

      If you don't know what `std::sort`'s comparison function has to look like, you can look it up on cppreference.
      https://en.cppreference.com/w/cpp/algorithm/sort
      Signature 3, Parameter "comp"
      I suppose your comparison function didn't satisfy the "Compare" requirement and therefore caused undefined behavior. It's hard to impossible for the compiler to diagnose such a mistake.

      • yeokaiwei

        "If you don't know what `std::sort`'s comparison function has to look like, you can look it up on cppreference.
        https://en.cppreference.com/w/cpp/algorithm/sort
        Signature 3, Parameter "comp"
        I suppose your comparison function didn't satisfy the "Compare" requirement and therefore caused undefined behavior. It's hard to impossible for the compiler to diagnose such a mistake."

        I thought std::sort worked like Excel and there was a default comparison.

        My mistake.

  • yeokaiwei

    This is for Quiz 2.

    Why doesn't Vector_Student.name work?

    • nascardriver

      `std::vector` doesn't have a `name` member. You need to select a student first, eg.

      • yeokaiwei

        Thanks nascar.
        I figured it out later.

        A compiler syntax warning would be great.

        E.g. Input Vector_student.name;
        Compiler warning (like Excel)
        Did you mean "Vector_Student[0].name;"?

  • yeokaiwei

    For Quiz 1,
    1. Why do we use enum instead of enum class?
    There is no explaination.

    2. Why do we use the example

    It contradicts previous warnings not to use namesapces.
    "Warning

    Avoid using directives (such as using namespace std;) at the top of your program. They violate the reason why namespaces were added in the first place."

    Inconsistent.

    3. Quiz 1 entry

    I hope this is acceptable.
    I wrote long-winded comments to explain my understanding.

    Thank you.

    • nascardriver

      1. "The enumerators are used as indexes of the array"
      Lesson 6.2 showed that if an enum is used for indexing, it's better to use `enum` than `enum class`.

      2. You quoted a warning about using directives (`using namespace`). The `using` here is a type alias.

      3.
      - If you don't modify a reference parameter, make it `const`. You can't call `CountTotalItems` if `Array_Player_Items` is `const`.
      Because you said you were using google naming:
      - Enumerators should be named like constants. Constants start with a 'k' followed by camel case. https://google.github.io/styleguide/cppguide.html#Enumerator_Names
      - Variables should be named in lower snake case. https://google.github.io/styleguide/cppguide.html#Variable_Names

      • yeokaiwei

        1. Got it, I'll have to remember that. Enum for index contradicts this statement from S.4.5a Enum classes. I remembered the following instead.

        "There’s little reason to use normal enumerated types instead of enum classes."
        https://www.learncpp.com/cpp-tutorial/4-5a-enum-classes/

        2. Got it. I think it might contradict the rule against "using namepace std".

        3. Thank you. From now on, I will add a style convention in the comments (it probably won't be Google style, although they took the effort to make one). If there was a Microsoft style or LCPP style, do let me know and I'll try to adhere to it as much as possible.

        I am using already using snakecase for my variables.

        However, I am against the One True Brace Style and prefer Allman as you don't have a missing brace when you read it.

        As for the enums, I used a enum_ instead of a k. Why? An arrow becomes karrow , art becomes kart, arma can become karma, icky becomes kicky, now becomes know, ite becomes a kite. Most importantly, id becomes kid.

        enum_anything lets one automatically recognise that is an enumerator name. You don't have to memorise  something. What in the world does a confusing k mean? It can even change the meaning of the enum.

        Google naming rules are bad. If you have another ruleset to follow, could you recommend it?

  • Rishi

    Why can't we use built-in array type for std::shuffle? I tried to use built-in array type and failed.Does std::shuffle() only support standard array type?

    • nascardriver

      `std::shuffle` works with built-in arrays. Without code and error messages I can't help you.

      • Rishi

        This is my code. I've tried passing pointers of the array to the shuffle function and that worked. The error message my compiler gave is below. Thank you.

        • nascardriver

          Built-in array don't have any `.functions()`. You need to use `std::begin` and `std::end`.

          • Rishi

            Oh thank you. I thought I could copy my error message and attach it using the new edit option. But you replied already. Just for reference, my error is below

            Thanks again

  • srt1104

    For the Blackjack question, I tried to modularize it as much as possible.
    I just saw your answer and noticed that my playBlackjack() function could also be broken down further but apart from that, can you suggest any improvements with my code? I am only posting the header files and main.cpp to keep it short. Thanks!

    constants.h

    card.h

    deck.h

    main.cpp

    Also there is a weird problem in my code. In main.cpp, there is a statement which is underlined with a scribbled line and it's not a warning nor an error or anything else (I've added a comment beside that line). Program compiles fine and runs fine. All it says is "C28020: The expression '0<=_Param_(1)&&_Param_(1)<=52-1' is not true at this call." when I hover my mouse over it. There is only one post on google matching it which goes to visualstudio forums and all they say is that it's not a bug. I'm using VS 2019 with C++ language standard set to C++17. Thanks again!

    • nascardriver

      main.cpp:13: This is either of

      Don't modify and use a variable in the same expression (Lesson 5.4). This can be fixed by doing

      main.cpp:21,22 and 30,31: Duplicate code. You can use a `while (true)` loop and `break` out of it once the input is not 'h'.

      The warning you're getting is trying to tell you that `deck[card_index++]` is overflowing `deck`, ie. `card_index` can be greater than `deck.size()`. This could happen if `getCardValue()` returns 0 or `min_dealer_score` is large. The warning will probably go away if you add a `&& (card_index < deck.size()` to the loop's condition. Visual Studio's overflow detection often causes false positives. In your case, the warning is not without reason.

  • Posti

    When I try this it just skips the input for element.forename and prints "Student #1 Name: Student #1 Grade: " in the console instead. It also doesn't print the student's forname, only the grades.

  • Sinethemba

    Here's my solution to question 2. Any feedback would be highly appreciated.

    • nascardriver

      - If an if-statement's body exceeds 1 line, wrap it in curly braces.
      - Pass/loop non-fundamental types by reference. Copying them is slow.
      - Line 37+: You never need an if-statement like that. It's equivalent to `return (A.studentGrade > B.studentGrade)`.
      - Line 71+: You don't need `gradeInvalid`. Use a do-while-loop with a call to `check_student_grade` as the condition instead.
      - Use single quotation marks for characters. Strings are more expensive.

  • Matthew

    Hello I was wondering if I could get some feedback for my answer for question 2? Thank you!

  • sergey

    "A better solution is to use std::array instead of built-in fixed arrays."
    Why are std::arrays considered better than standard arrays?
    In order to pass a std::array to a function you need to specify in the function's arguments the size of the array you expect, isn't this a more limited functionality compared to normal arrays?
    For example:

    vs

    • nascardriver

      If you don't know the size, use `std::span`. It works for many different containers.

      `std::array` is better, because it doesn't decay and has many member functions that make it easier to use.

  • Imagine

    Solution for question 7a:

    My solution works but its not like the tutorial, please point out my mistakes.

    • nascardriver

      - Line 121: Magic number. Use `myDeck.size()`.
      - Line 121+123 causes undefined behavior. There is no index 52.
      - If you only need the index variable of a for-loop to index an array. Use a range-based for loop instead. This would have prevented the previous 2 notes.
      - `shuffleDeck` isn't random. Don't re-seed random number generators.
      - `shuffleDeck` should be `void` or return by reference. There's no point in copying the already modified deck to return.
      - Avoid `static` local variables unless they should never reset. `drawCard` isn't reusable and will caused undefined behavior if you call it too often.
      - Use ++prefix unless you need the result of postfix++
      - Line 217, 227, 247 are unconditional
      - Line 240-243: You never need an if-statement for this. `return (playersTotal > dealersTotal)`.

  • Choi

    IDK. But this code won't work