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.

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.
Hint: Don’t forget to call srand() at the top of your main function.
Hint: If you’re using Visual Studio, don’t forget to call rand() once before using rand.

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

411 comments to 6.x — Chapter 6 comprehensive quiz

  • Peter Baum

    Hi Alex,

    I completely understand why you would want to limit this site and course to just learning CPP.  Doing so means that you don’t have to get entangled in specific IDEs, compilers, and operating systems.  HOWEVER, let’s think about why people actually come to the site.  I believe almost nobody comes just to learn CPP.  Probably almost all of us come because we either have a specific program or programs we want to write, or we want to find employment where we will be asked to write specific programs.  Learning a language and being able to write useful programs are very different goals.  

    I see this issue playing out in a number of ways:

    1. There is very little information about IDEs and operating system specifics that are needed to write real programs that would interest anyone.  Fortunately, you and others have given some help in the comments section and, of course, there is always stackoverflow.  Integrating this information into the lessons would be more effective.

    2. You can’t learn to be a good programmer without writing lots of programs.  Writing short exercises is not the same as writing a useful program.  Useful programs typically require more sophisticated I/O, require more error checking, require more sophisticated data structures, and require more thinking about data structures and logic.

    I’m not necessarily suggesting a lot more work on your part.  For example, the simplified blackjack program you came up with could be easily turned into the full game; it isn’t that much more complicated.  Even with error checking and Unicode output, my version was only about 1100 lines of code.

    3. One can’t effectively separate out language constructs from understanding how those constructs or collections of constructs can be used to solve problems.  The site feels too much like a dictionary rather than a place people can learn about prose and poetry with a dictionary at hand.  Yes, I understand that this greatly complicates things, because you will have both novices and people with a great deal of programming experience coming to the site.  In spite of this difficulty, I think there are practical ways to address the problem, and making the site useful to students with a variety of backgrounds would be extremely beneficial.

    4. The overall goals, I believe, should include making the learning of the material as motivating and as easy as possible.  I think the most motivating thing about programming is to write useful and interesting programs and see them function well.  This idea, if taken to heart, means reorganization of the current material and integrating it with a series of useful programs.

    5. I envision multiple paths through the material: beginner, moderate experience, experienced, reference.  You have done a little with this idea when you have added optional material.  More could be done.

    6. We communicated earlier about upgrading the comment section by dividing the comments by topics such as typos, going further, improving the lesson, questions about specific issues or topics, and questions about specific code.  I also mentioned that some of the comment areas are now so large that they are completely ineffective, i.e., nobody is going to wade through hundreds of comments, almost all of which are irrelevant to their purpose.

    If you want to talk more about these issues in detail, either here, via e-mail, or phone, I’d be glad to help in any way I can.  In the meantime, on to lesson 7.

    • Alex

      As always, thanks for the thoughts and feedback. If nothing else, they help me question why I have made certain choices, and re-evaluate if those choices were correct in light of how things have evolved.

      Given enough time, I would undoubtedly do many things differently. I am not as good a steward as I would like to be, but that's simply the reality of having a full time job, and a family, and this being a passion project that I can only devote limited time to.

      I hope that in the future I will be able to do a 3rd edition of this site (this is currently the 2nd edition). I will bookmark this comment and return to it should that become a viable path, as there are many good suggestions here.

  • Peter Baum

    nit-picking here...
    Regarding:

    "7b) In actual blackjack, if the player and dealer have the same score, the result is a tie and neither wins."

    This is only true for values less than or equal to 21.  If both player and dealer bust (regardless of whether or not they are equal), the dealer wins.  That is the house's edge.

  • Will

    Hi Alex

    "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 believe that the answer to question 7a) doesn't answer the question. The answer doesn't actually handle a case where an ace is dealt; rather, it just counts whether the player's total card value has exceeded 21. For instance, if the player 'hits' on 19 and is dealt a 3 of clubs they will exceed 21; consequently the program will think that an 'ace' has been dealt, decrease the 'ace' counter to 3 and then subtract 10 from the players total card value leaving them with 12 (22 - 10). Not only are there still 4 aces left in the deck, the player is effectively given 4 free chances to try to improve/change their score.

    Another consequence of the answer is that the value of an ace can never be equal to either 1 or 11 it is only ever equal to 11.

    In sum: the program counts aces where none might have been dealt, changes the players score unnecessarily, saves the player from going bust and the value of an ace can never be 1 but is only 11.

    Despite my criticism, your tutorials have been extremely helpful and has taken me from knowing nothing about coding to being able to create a whole program(with some guidance of course :)). I am very grateful for the time and effort invested in these tutorials.

    I have attempted to come up with an answer of my own though and I believe I've found one.
    Essentially my solution just measures the difference between the players current total card value against their previous total card value to determine whether an ace has been dealt (an ace will always result in a difference of 11).

    Its not perfect but it does the job in principle.

    • Alex

      In the proposed solution, we keep track of how many aces the player was dealt. We only subtract 10 when the player goes over 21 if they have a non-zero ace counter, meaning that they were dealt an ace. No freebies here.

      In this solution, aces are assumed to be worth 11 until that "busts" the player, and then they are converted to a value of 1 (by subtracting 10 from that player's score). Can you think of a case where that doesn't work?

  • Benjamin Collins

    Hey Alex, gotta say thanks for making these tutorials. The little words of encouragement at the start of this quiz were really nice. It's easy to get distracted and shirk learning, despite actually really enjoying programming.

    As a suggestion, maybe you should put something at the start of chapter 6 that lets people know exactly what they're getting into with this one. A nice little "this is gonna be the hardest one yet, so buckle down" to help keep people from getting discouraged or from feeling inadequate for struggling. It would help people keep the discipline and willpower to finish this one because they can look back and think, "it's okay that this is a little confusing, this is one of the harder ones." Something like that at the beginning rather than the end would really do wonders for helping people learn. Thanks again!

    • Alex

      Good idea.

      I've also been toying with the idea of breaking this chapter up into two subchapters... but I'm not sure what a logical subdivision would be.

      • Benjamin Collins

        It might make sense to separate the parts about pointers (as well as the two parts on references) and the parts about arrays into two separate sections. It's hard to tell which would come first. You could say that the pointer chapter should come first, so as to avoid jumping between two subjects when dynamic arrays crop up. However, since the chapter before this one is about loops, it's also logical for the section on arrays to come first since they tie into each other really well. It's a tough call, but there's gotta be a good solution to somehow make this chapter more easily digestible.

  • Josh

    I am getting a compile error on 6H whenever I call the function getCardValue in the main function.printDeck() function works as intended but whenever I call getCardvalue I get a compile error.
    Here is a snippet of the code.

    Here is the error: invalid initialization of reference type 'const Card&' from expression of type 'Card*'. I am stuck as to why this happened since printDeck and getCardValue  is essentially the same.

  • Josh

    Hi in 6b solution, why is it still necessary to call the enums Cardrank and CardSuit?

    • nascardriver

      Hi Josh!

      Nothing is being called in 6b. Two variables of type @CardRank and @CardSuit are created. Those will later be used to store the card's rank and suit.

      References
      * Lesson 4.7 - Structs

      • Josh

        Thank you. I was always having trouble with enums, thinking they are just there for a more readable code. Now I can see them as another variable type. Cheers.

  • Will

    Hello. below is the code for my answer to question 4. Its quite different to the model answer.
    So, did I answer the question correctly, or did I misunderstand it?

    From what I can tell my solution reaches the same outcome as the model answer. Why is the model answer written in the way it was?

    I would appreciate any feedback.

    • nascardriver

      Hi Will!

      You missed "Stop when you hit a null terminator". Instead, you calculated the string length beforehand (which could've also been done using @std::strlen).

      Suggestions:
      * @main: Use uniform initialization.
      * @string Bad name, could cause naming conflict with @std::string.

      • Will

        Hi nascardriver!

        I thought that since a c-style string will stop when it hits a null terminator the "Stop when you hit a null terminator" aspect of the question would automatically be fulfilled.

        I see now that the difference is that my code prints the null terminator and then stops; where as the model answer stops before the null terminator (the null terminator is used as a condition to stop the loop printing the string and not the string itself).

        A slightly ambiguous part of the question perhaps?

        In any case, thank you for your response, it helped me understand the question better.

        P.S. I will keep your suggestions in mind going forward.

  • Ishak

    getCardValue(*cardPtr++);

    Isnt this bad because it has 2 operations with side effects?

    Also will practice actually improve my coding skills? I was able to write it, but it wasn't as neat as the one you wrote.

    • Alex

      No, it's fine. The general rule is: avoid using a variable that has side effects applied more than once in a statement (e.g. i + i++)

      This expression has multiple side effects, but the variable itself is only used once, so it's fine (the side effects will be applied in precedence order, there's no potential ambiguity).

  • Peter Baum

    Here is something else that I tried to do that others may want to be able to do with their programs.  How do you change the font size and other characteristics when using cout or wcout?  I tried finding examples online and I think I am close but I get a message about wcscpy_s not a member of std.  I've run into this kind of thing before.  How do you find out where headers or library members are for a function?

    Here is the code I have so far:

    • nascardriver

      Hi Peter!

      > How do you change the font size and other characteristics when using cout or wcout?
      This has nothing to do with any standard C++ feature, this is terminal/console specific so you'll have to find a library offered by your terminal/console developer (In this case Microsoft). Same goes for colors.

      > How do you find out where headers or library members are for a function?
      Look it up in a documentation
      From what I can tell wcscpy_s is a C function and wcscpy is it's C++ counter part.
      microsoft - Table mid page - https://msdn.microsoft.com/en-us/library/kk6xf663.aspx
      cplusplus - Top right - http://www.cplusplus.com/reference/cwchar/wcscpy/

      PS: IIRC VS has an option to use wide strings by default so you don't need to prefix every string with an 'L'. I'm suggesting this, because your code is platform dependent anyway so there's no need to avoid compiler features.

      • Peter Baum

        Making progress but still some struggles.  I got the unicode to work on the blackjack program along with some of your other great suggestions, such as making an array of pointers.  Regarding the above.

        1. wcscpy_s has the s to indicate that it has security features.  It is CPP.  My compiler requires I use that version unless I disable the security features, which I don't want to do on principle.

        2. Although I still want to figure out how to change the font and size from the CPP program, as a work around for now, I can right click in the header area of the DOS box and get a properties menu that lets me change the font and size.

        3. I can change the color using std::system("COLOR xy"); where x and y are hex digits that stand for background and foreground colors.

        4. I tried making the compiler default to wide characters and I saw examples online about doing this but that menu item didn't show up with VS Community 2017.  I got it to work with TCHAR and wcout, so it is something perhaps to look into later.

    • Alex

      If all you want are basic colors (RGB, CMY) you can probably use the curses I/O library. A lot of roguelike games use this.

      If you're looking for more (font support, sizes, etc...) then you'll either need to use terminal/console that supports this (and configure it appropriately), or you'll need to find or create your own graphical window and text rendering routines.

  • Peter Baum

    quiz item 6H alternative
    Since there is a relationship between rank and the values we want, we can do something simple like the following rather than use the inefficient switch statement.  Here I assume r is an int that holds the rank, with ace of rank 1:

Leave a Comment

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