Search

6.x — Chapter 6 comprehensive quiz

Words of encouragement

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

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

Chapter summary

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

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

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

Arrays can be made multidimensional by using multiple indices.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Quiz time

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

Show Solution

2) Write the following program: Create a struct that holds a student’s 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:

Joe
82
Terry
73
Ralph
4
Alex
94
Mark
88

The output should look like this:

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

Hint: You can modify the selection sort algorithm from lesson 6.4 -- Sorting an array using selection sort to sort your dynamic array. 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?

a)

Show Solution

b)

Show Solution

c)

Show Solution

d)

Show Solution

e)

Show Solution

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

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

Show Solution

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

Show Solution

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

Show Solution

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

Hint: Use static_cast if you need to convert an integer into an enumerated type.
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
Index
6.16 -- An introduction to std::vector

585 comments to 6.x — Chapter 6 comprehensive quiz

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

    • Arthur

      after a lot of testing I turned up two glitches , one a low score tie where if player hold on a silly low score like 8 and the dealer was dealt an 8 , the game would end as draw with no second card to dealer. the second glitch was a bust on deal glitch dealt two Aces I held it flipped the playerTurn bool then fell through after choosing softAce option. some fixes...

      line 192

      line 268

      between lines 249-250

      lowTie was set at 12 so any hand at 11 or less that is a tie the dealer will draw a card

    • * Line 67, 69, 313: Initialize your variables with brace initializers. You used copy initialization.
      * Line 54, 55, 158: Initialize your variables with brace initializers.
      * Line 60, 310: Use std::rand instead of its global-namespaced counterpart.
      * Line 310: Use std::srand instead of its global-namespaced counterpart.
      * Line 310: Use std::time instead of its global-namespaced counterpart.
      * Line 268, 274, 274: Don't compare booleans to false/true.
      * @Suit, @Face should be enum classes.
      * Line 66ff, 315ff: Use curly brackets when a body exceeds 1 line.
      * Line 78: Use your enum.
      * You don't need to return from voids.
      * Line 138: Should be else if.
      * Line 7-8: Should be constexpr.
      * Line 145-157: There are some constants here, make them constexpr.
      * Line 150: Static block-scope variables should be avoided as they can break code in a multi-threaded environment.
      * Line 167-173 and 177-183 are almost equivalent. Use a function.
      * Inconsistent formatting.
      * You're using the same name style for variables and function. This can lead to confusion.
      * Line 323-327:

      I didn't check the logic.

      > there is a possibility of an exception
      Fix it, even if it's just a message saying that X went wrong. No program should contain unhandled problems.

  • Arthur

    I am working on the tutorial and the method suggested here for the learning value of it , but I just wanted to know if there is anything particularly wrong with making a deck of card in a different manner. I took a break from the tutorial and came up with this for a deck of shuffled cards. it is all still just numbers but just a few switch statements away from printing as cards. I used the easiest random and just made it shuffle through three times. really just curious to know if there is something wrong with this way?

    • * Line 9, 10, 11: Initialize your variables with brace initializers.
      * Line 21: Use std::rand instead of it's global-namespaced counterpart.
      * Line 21: Use std::time instead of it's global-namespaced counterpart.
      * You don't need to specify the array size when fully initializing arrays.
      * Magic numbers: 3, 4, 13, 51, 52 and those in @face. Declare constants, enums, and use @std::size.
      * Line 16: Return implicitly casts away unsigned.
      * C-style arrays have less functionality than @std::array. If you like them, use them, but you might have to write more the get the same result.
      * Inconsistent formatting. Use your editor's auto-formatting feature.

      Other than you used a C-style array, you're doing pretty much the same as Alex.

      • Arthur

        so for the random number generating mine should look like this?

        srand(static_cast<unsigned int>(time(nullptr)));

        return static_cast<int>(rand() / ((RAND_MAX + 1u) / 51));

        and the problem is that the way I have it is converting the number from unsigned to signed, which may or may not cause a problem so I should avoid the conversion out of good practice?

        it is not actually a preference for  "C-style arrays" it is simply using the one that I am less confused with. I have made a functioning game of Black Jack working from what I posted , now I will try to rebuild it using the lesson parameters so I am not just copying what Alex posted. mine is a bit of a mess but I did get it displaying the cards drawn their face value , the players current score , giving option to 'soften' the Ace if getting hit caused bust, option to replay after each hand, and shuffles the deck when cards are running out, I gave the tie to the dealer. it plays ok, displays organized in the console , but does need exception handling still.  

        one thing that confused me in this chapter and am curious if I understand it better now?

            using index_t = std::array<Card, 52>::size_type;
            index_t card = 0;

        what " using index_t = "
        means is I am giving a name to
        "std::array<Card, 52>::size_type;"

        and could read (just for fun making the point ;-)

        using a_short_name_instead_of_typing = std::array<Card, 52>::size_type;
        a_short_name_instead_of_typing card = 0;

        • The problem was the implicit cast. Implicit casts can cause undesired behavior, especially signed/unsigned casts. It's good practice to avoid conversions altogether, but you need a type larger than int to store (RAND_MAX + 1), so there's no way around a cast in this case.

          > using
          Correct. This was covered in lesson 4.6.

  • Arthur

    in the solution for question two I am curious about

    #include <utility> // include <algorithm> instead if not C++11

    I am unclear on  using 'algorithm' or 'utility' and don't know what "if not C++11" means is algorithm oldder and people using C++14 -> C++17 be using 'utility'? either works for me...

    got pretty close on the first two quiz programs here but find I have to peek to get through them, I dont know but are things like

    or

    obvious?  I had the idea that is what I should be able to do but had to peek to get the syntax straight.

    • @std::swap was moved from @<algorithm> to @<utility> in C++11. If you're using C++11 or later, @std::swap might still be declared in @<algorithm>, but it doesn't have to.

      There's no problem with peeking at the solution. It will take some time and practice to know the syntax by heart.

  • usedName

    Hello!

    After a few months of going through these (wonderful) tutorials I am not able to solve the game card-related questions of this quiz without taking a peek at the solutions (or more than a peek, hehe :( ). I can understand the code and what it is supposed to do, but it's really hard for me to write it (the code) using only my brain. At this point I'm really wondering if I will ever be able to become decent at programming. Is it okay if after repeated failures I go to the solution, read the code, understand it and then try to reproduce it?

    I really like programming (even if it's sooooo frustrating sometimes) but I don't really know if I have the right brain for it.

    • Alex

      Yes, the solutions are provided precisely so you can learn from your mistakes and not get stuck. As long as you're understanding the solutions and where you went wrong, you're learning -- and you simply need more practice.

  • X

    Can someone explain to me what this is and what it does? All I understand in these two lines is that there is an array of type Card with 52 members. I don't understand what size_type is and why it's necessary, why we are naming this index_t, and how it is possible to set the array alias which has already been declared to have 52 members to 0.

    • @std::array::size_type is the integral type used to store an @std::array's size (Most likely an unsigned int).
      @index_t is used as an alias to type less.
      @card is a variable of type @index_t with value 0.

      • X

        Thanks for the quick reply! I still have a few questions though. I'm actually trying to make a War game with these concepts but I'm struggling with understanding just this one section (my previous comment + the code below).

        As for the current questions: I don't get why we need to store the std::array's size. Didn't we already declare it to be 52? And I understand that index_t is an alias but an alias is just an illusion, right? Arrays can't be given variables as far as I know, so how are we giving an alias (which is just an array) a variable? And why do we even need this variable in this way? Wouldn't it be easier to simply create a indexing variable completely separate from this alias stuff?

        As for the code below, I get what the two loops are doing but within the second loop (the first two lines), what exactly is going on here? The way I see it, we have a deck array. At whatever position the indexing variable "card" is in (0, to start), we access the corresponding struct member. However, what are we static casting on the right hand side? What's the purpose of this static cast? For the first of the two lines, I am reading it as we are static casting the CardSuit type onto the suit integer, but aren't enums already integers? What's the point here? When I use cout, I still get integer output.

        Sorry if I'm asking too many questions. I am seriously stumped though. I've been able to understand everything on this site thus far but looking at this code and Googling has given me no answers.

        • > alias
          You could type out

          everywhere, but that's too much typing, hence the alias.
          We're not giving @std::array a variable, we're giving it a type, because it needs to know what it's made of.

          > we access the corresponding struct member
          array member

          > cast
          @suit is an int, @deck[card].suit is a @CardSuit. Implicit casts are evil, use explicit casts.

  • pakraaw

    Here's my printCard function. I used a different logic for the switch outputting the rank of the card.
    Is this correct? Advisable?

    • * Line 26: Initialize your variables with brace initializers. You used copy initialization.
      * Line 27, 28: Initialize your variables with brace initializers.
      * Line 31: Limit your lines to 80 characters in length for better readability on small displays.
      * @rank_code and @suite_code should be chars.
      * You're using the same naming convention for types and function or variables. This led to confusion.

Leave a Comment

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