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

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.

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

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

  • Sujith

    Hi! I'm just here to say thank you for the words of encouragement! God, it's been a stressful chapter despite having the programming experience. I've clearly learned a lot. Thanks again for the words. I just love this tutorial <3

  • OmegaX128

    This took me five hours to complete. How does this look?

    • OmegaX128

      Oops, line 248 doesn't do what it should:

      It should be:

    • * Initialize your variables with brace initializers.
      * Use enum class.
      * Declare an alias for your deck type.
      * Magic numbers: 51, 52. Use deck.size().
      * Inconsistent formatting. Use your editor's auto-format feature.
      * If an if- or for-statement's body exceeds 1 line, wrap it in curly brackets.
      * Declare only one variable per line (Unless they're uninitialized).
      * Use constexpr for const variables that are compile-time constant.
      * @ace is supposed to be dynamic. An ace either counts as 1, or 11, whichever is better for the player.

  • Lorenz

    Solution for 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.

    I adjusted the function getCardValue:

    and then when Player or Dealer play their card I pass the current score.

    seems neat to me, does it make sense?

  • Lorenz

    your solution for 6G is pretty complicated (I will never be able to think it):

    I came up with something simpler:

    considering my noobness... is there anything wrong in the way I did it?
    Thanks

    • @deck.at doesn't want an int as parameter, it wants an @std::array<Card, 52>::size_type. Either change the type of @i and return type of @getRandomNumber or add casts.
      Magic numbers: 52, 51, Alex did this wrong too.
      You're using copy initialization, Alex did this wrong too.

      • Lorenz

        so if Deck.at wants a @std::array<Card, 52>::size_type,
        why my code actually works?

        thank you

        • @std::array<Card, 52>::size_type is an @std::size_t, which is most likely an unsigned int (What it actually is is implementation defined!).
          int is implicitly convertible to unsigned int. You should get a compiler warning, because this conversion can in some cases be undesirable.

  • Sammy

    I'd like to point out on Quiz 1 that if you use "enum class ItemTypes",

    you have to "static_cast<int>(ItemTypes::MAX_ITEMS)" for the array index.

  • pickyPrinter

    I found an alternate way to print cards as actual cards through the creative use of symbols:

    !Spoiler alert if you have not completed the quiz!

    (If my style is bad feel free to tell me)

  • Artun

    For 6c) I set up my enum classes this way first to see if it works, and if could avoid the switch statements. To my surprise it did, I thought the enumerator could only be integers from the lessons, although it was not explicitly said that we could not do it. Anything wrong with doing it this way?

  • Tommy

    Hello

    This is a treasure trove of a website, thank you for this.
    Question regarding 6d. I'm confident I can write a cruder, closer-to-C way to initialise a deck of cards, but your proposed solution throws me a bit. Can you tell me in layman's terms what is happening in the code below?

    I get that we're using a type alias, and that we're setting to 0 in prep for a count, but an alias of type array just does't want to make sense to my brain. Thanks in advance!

    • Hello Tommy!

      It's not an alias for the array type. It's an alias for the type that the array uses for indexing.
      Let's say we had an alias for the array type

  • Aaron

    Hey all, first time commenting. Really appreciate the tutorials.

    This is the shuffleDeck function off the top of my head and wondering if you could tell me why it may not be optimal.

  • Red Lightning

    I have a question about the quizzes in general:
    Can I customize my answers to them so they will look a bit differently (e.g.
    Instead of writing that [NAME] got a grade of [GRADE], to write Student number #[student index + 1]:
    Name: [NAME]
    Grade: [GRADE]
    )?

    I am asking this because I do it most of the time and I want to know if doing so affects my learning badly.

  • C.E

    I have a weird error in the hitnstand function definition , I use an if statement to test comparability , for some reason whilst running on debug mode through main ( std::cout << hitstand) . Upon user input being 0 , it has a tendency to output a value of 1. I'm quite confused i've put the variables in question (hit and stand) on the quick watch and it doesn't seem to indicate a particular change from 0 -1 . Quite confused.

    • * Line 122, 123, 165, 167, 168, 179, 207, 221, 229, 41, 42, 81, 89, 91: Initialize your variables with brace initializers.
      * Line 1, 89, 252, 253: Limit your lines to 80 characters in length for better readability on small displays.
      * @index_t and an alias for the array should be declared globally.
      * Line 230+: Add curly brackets.
      * @CARD_RANKS, @CARD_SUITS should be enum class.
      * Line 81, 82: Initialize.
      * Initialize random number generators only once. @shuffleDeck yields the same results when called multiple times in 1 second.
      * @getCardValue: Missing return statement. Make sure you followed lesson 0.10 and 0.11.
      * @hitOrStand should return a bool.
      * Line 159: This function returns ints, not bools.
      * @shuffleDeck doesn't use @a.
      * Array indexes start at 0.
      * Comparisons use ==, = is an assignment.

  • Dimbo1911

    Any advices or remarks on the second question? Thanks for your time.

    • * Line 7, 8: Initialize your variables with brace initializers.
      * Line 30, 31: Don't swap members of the objects, swap the entire object at once.
      * Line 41-42: Wrap in curly brackets.
      * @outputSorted: Misleading name, this function is unrelated to sorting.

      You don't need to write a sorting function every time you want to sort something. Use @std::sort from @<algorithm>.

  • Dimbo1911

    First assignment, seems easy enough

  • I have a more elegant solution for 6c.

    It of course requires global variables but I find it more efficient if printing is done frequently enough (less checks are being made).

    P.S. Thank you very much for the tutorials!

    • Alex

      Once you get into classes, you can do this without global variables, by making the suits and ranks arrays static members of the Card class. Seems like a nice optimization for conciseness without losing any maintainability.

  • Paulo Filipe

    For exercise 6d) why does a regular int work as the array index without any cast to size_type?

    It compiles without throwing a single warning using gcc compiler (c4droid for Android. Didn't try in other compilers)

    Is it extremely wrong not to cast?

    • int can be implicitly cast to size_type (unsigned int). It works and shouldn't cause problems. But you limit the range you can access and you could accidentally try to access negative indexes.

      • Paulo Filipe

        Humans... You lazy creatures... Making me ask lazy questions just because of your nature... Pffff...

        What if we static_cast<unsigned int> instead? Easier than the looooong cast to size_type?

  • Ishak

    Hey Alex, I don't know how knowledgeable about this you are, but I'll ask for your opinion nevertheless. I am studying computational engineering, and Some people use python with C++ while others use modern Fortran. what do you think is a best programming language for numerical analysis in terms of speed, syntax, and general ability when it comes to numerical methods, and what do you think I should focus on learning as a student [I'll have to do projects soon and I need a relatively easier language to be soon adequate in]? thanks in advance

    • Hi Ishak!

      C++ is great for large projects and high-performance applications. Not so much for a quick project when you just want to get a task done, because you generally need to write more code than in other languages.
      I like to use python for the things you mentioned. It's easy to learn, has lots of libraries and scripts are typically written within a couple of minutes.
      Many people like R for statistical programs, I'm not a fan of it, but you can give it a look.

  • Nathan

    This is my answer to the first quiz part.  I wrote it in notes on an iPad, and tested it in repl.it c++11

    • * Line 45, 48, 50: Initialize your variables with brace initializers. You used copy initialization.
      * @Items should be an enum class.
      * Don't mix types in an array. Remember, each item type can only appear once in your inventory.
      * Don't mix prints like that. A function called while printing shouldn't be printing itself.

  • Nathan

    Could i just put my array into a namespace and call it from there instead of repeatedly passing it in large programs?

  • C.E

    Although this is an incomplete solution to ex2 I was wondering what the for loop did in  line 33. I belive that on line 53 ( well the comment is incorrect as is stands , but I perform a pointer to a pointer operation) . One  * operation for the type ( i.e a pointer to type "int" ) and the other to access the value of the pointer.  Is the output  the pointer pointing to the array ( and if so what would be the output. It seems to be printing out  garbage addresses ?

    • Both asterisks (*) are part of the type. @studentsArray2 is a pointer to a pointer to a @Student (Or a pointer to an array of @Student). The ampersand (&) gives you the address of @studentsArray.

      The loop in line 33 loops @numOFEntires times. In each iteration, it prints the pointer that @StudentsArray2 is pointing to. After each iteration, it moves @StudentsArray2 4 or 8 bytes forward, depending on your architecture. Neither the body nor the increment makes sense, because @StudentsArray2 is not an array.

      • C.E

        Ah that seems to make sense, so if  I added a double deference here it would deference the pointer to access the array determined in line 51.Although it would access the first element due the pointer decay scenario.  For some reason double deference seems to cause an operator overload.

  • Max Morgan

    Why dooes the first for loop not stop after it reaches MAX_SUIT? I understand that we don't want that, but since there are only 4 card suites, how does it continue to run after it hits the MAX_SUIT enum?

    • Alex

      It doesn't.
      First, suit is 0, and then the inner loop (rank) runs to completion.
      Then suit is 1, and the inner loop runs to completion again.
      Then suit is 2, and the inner loop runs to completion again.
      Then suit is 3, and the inner loop runs to completion a final time.
      Then suit is MAX_SUITS, and the loop terminates.

  • JMEllis

    I tried Question 2 by old-fashioned malloc'ing, but I'm finding that it crashes. However, it works fine if I allocate with new. I can't see why! Does the following look OK?

    Does the struct's std::string memory allocation take care of itself?

    • @malloc allocates memory, nothing else. You have to use a placement new, since C doesn't know constructors.

      Your typedef doesn't do anything.
      You cast is unsafe.
      Your initialization is unsafe.

      Are you trying to learn C? If so, I'll stop pointing out problems that are solved by using C++ and you should stop mixing C and C++, because this is what it gets you.

  • whoon

    I found a way to use std::sort for sorting grades, just sharing it for whoever needs it:

    • Hi!

      Instead of creating a struct with @operator(), you could also just declare a function.

      Or use a lambda function (Not covered by learncpp), since you're not going to re-use @gradeSort anyway.

      • whoon

        Oh, thank you for this. I just followed another site's way of creating the struct, but this way seems easier and cleaner.

        Also, this I did the shuffling like so:

        Is that way okay? Thanks.

        • * Line 5: Limit your lines to 80 characters in length for better readability on small displays.

          You should add an alias for the deck type, so that you can easily change it in the future. If you want to, you can also move @index_t outside of the function, because you'll need it in multiple places.

          Line 5 shouldn't hardcode 51, use (deck.size() - 1) instead.

  • oliver

    Hi. For question 2, I saw the solution and it was very neat. However, for the sorting function, I did not use a function, but rather kept everything within int main().
    This is my code for the sorting function, and I was hoping if you could tell me if its a good solution, or that there are things to be improved, or maybe I'm doing something wrong. Thank you

    • Hi Oliver!

      Use the array syntax to index arrays, it's easier to read.

      You should swap the entire student, not individual members. Your code will break if more members are added to the student struct.

  • paparob76

    Hi, on quiz question 1, what and where is the stdafx.h file? My compiler complained that it could not be found. I deleted the #include line and the program ran fine. I noticed that its is also in question 2 answer.

    Also, in question 2, my compiler complained that the struct wasnt initialized. When i initialized them to {0}, the program ran fine. Why is that?
    Thanks

    • Hi!

      question 1
      @"stdafx.h" was used by Visual Studio's compiler. It has been replaced by @"pc.h".
      Ideally, precompiled headers should be disabled in the project settings, rendering both includes unnecessary.

      question 2
      Line number and warning/error please.
      All variables should be initialized. If you're talking about line 7-8, initialize the variables with empty curly brackets to use their default value.

      • paparob76

        Hi nascardriver,

        yes, those were the lines that needed initializing, thanks for that.

        I am having issues with question 7. Lines 41,58 and 107, my compiler says that MAX_SUIT and MAX_RANKS are not handled in the switches. How do I do that?

        Also, in line 97, my compiler says "error: conversion to 'index_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]" What should I do about that?

        Thanks!

        • > not handled in the switches
          Add a default case

          > sign-conversion
          Add a @static_cast to @index_t

          • paparob76

            Hi nascardriver,
            After adding a static_cast to the line, i get the following error messages:

            In function 'void shuffleDeck(std::array<Card, 52>&)':|
            error: expected unqualified-id before 'static_cast'|
            error: 'swapIndex' was not declared in this scope|
            These errors are for line 6.

            Here's how I did it:

            I'm confused, because here is where we declared swapIndex.
            A little help please?
            Thanks.

            • * Line 4, 6: Initialize your variables with brace initializers. You used copy initialization.

              @static_cast is a function, you forgot the parentheses and it's in the wrong place.

              You might want to re-read lesson 4.4a, casts are important.

              • paparob76

                Hi nascardriver,

                I rewrote the code with the code you suggested and am still getting errors for that line:

                For line 6:

                In function 'void shuffleDeck(std::array<Card, 52>&)':|
                error: narrowing conversion of 'getRandomNumber(0, 51)' from 'int' to 'index_t' {aka 'unsigned int'} inside { } [-Werror=narrowing]|

                error: conversion to 'index_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|

    • Alex

      Those shouldn't have been there any more. I've removed them.

  • Arthur

    here is my try at a black jack game it is functional will play through the deck and shuffle when the deck gets low there is a possibility of an exception but the window on that is pretty slim I think the round would have to start at the threshold of the cutoff then also have to use ten + cards. I tried improvising with the enum Face adding in a joker card to offset the values then used the enum Card.cardface value to calculate scores, the joker is not built into the deck.