Search

10.3 — Arrays and loops

Consider the case where we want to find the average test score of a class of students. Using individual variables:

That’s a lot of variables and a lot of typing -- and this is just 5 students! Imagine how much work we’d have to do for 30 students, or 150.

Plus, if a new student is added, a new variable has to be declared, initialized, and added to the totalScore calculation. Any time you have to modify old code, you run the risk of introducing errors.

Using arrays offers a slightly better solution:

This cuts down on the number of variables declared significantly, but totalScore still requires each array element be listed individually. And as above, changing the number of students means the totalScore formula needs to be manually adjusted.

If only there were a way to loop through our array and calculate totalScore directly.

Loops and arrays

In a previous lesson, you learned that the array subscript doesn’t need to be a constant value -- it can be a variable. This means we can use a loop variable as an array index to loop through all of the elements of our array and perform some calculation on them. This is such a common thing to do that wherever you find arrays, you will almost certainly find loops! When a loop is used to access each array element in turn, this is often called iterating through the array.

Here’s our example above using a for loop:

This solution is ideal in terms of both readability and maintenance. Because the loop does all of our array element accesses, the formulas adjust automatically to account for the number of elements in the array. This means the calculations do not have to be manually altered to account for new students, and we do not have to manually add the name of new array elements!

Here’s an example of using a loop to search an array in order to determine the best score in the class:

In this example, we use a non-loop variable called maxScore to keep track of the highest score we’ve seen. maxScore is initialized to 0 to represent that we have not seen any scores yet. We then iterate through each element of the array, and if we find a score that is higher than any we’ve seen before, we set maxScore to that value. Thus, maxScore always represents the highest score out of all the elements we’ve searched so far. By the time we reach the end of the array, maxScore holds the highest score in the entire array.

Mixing loops and arrays

Loops are typically used with arrays to do one of three things:
1) Calculate a value (e.g. average value, total value)
2) Search for a value (e.g. highest value, lowest value).
3) Reorganize the array (e.g. ascending order, descending order)

When calculating a value, a variable is typically used to hold an intermediate result that is used to calculate the final value. In the above example where we are calculating an average score, totalScore holds the total score for all the elements examined so far.

When searching for a value, a variable is typically used to hold the best candidate value seen so far (or the array index of the best candidate). In the above example where we use a loop to find the best score, maxScore is used to hold the highest score encountered so far.

Sorting an array is a bit more tricky, as it typically involves nested loops. We will cover sorting an array in the next lesson.

Arrays and off-by-one errors

One of the trickiest parts of using loops with arrays is making sure the loop iterates the proper number of times. Off-by-one errors are easy to make, and trying to access an element that is larger than the length of the array can have dire consequences. Consider the following program:

The problem with this program is that the condition in the for loop is wrong! The array declared has 5 elements, indexed from 0 to 4. However, this array loops from 0 to 5. Consequently, on the last iteration, the array will execute this:

But scores[5] is undefined! This can cause all sorts of issues, with the most likely being that scores[5] results in a garbage value. In this case, the probable result is that maxScore will be wrong.

However, imagine what would happen if we inadvertently assigned a value to array[5]! We might overwrite another variable (or part of it), or perhaps corrupt something -- these types of bugs can be very hard to track down!

Consequently, when using loops with arrays, always double-check your loop conditions to make sure you do not introduce off-by-one errors.

Quiz time

Question #1


Print the following array to the screen using a loop:

Hint: You can use std::size (as of C++17) or the sizeof() trick (prior to C++17) to determine the array length.

Show Solution

Question #2


Given the array in question 1:

Ask the user for a number between 1 and 9. If the user does not enter a number between 1 and 9, repeatedly ask for an integer value until they do. Once they have entered a number between 1 and 9, print the array. Then search the array for the value that the user entered and print the index of that element.

You can test std::cin for invalid input by using the following code:

Show Solution

Question #3


Modify the following program so that instead of having maxScore hold the largest score directly, a variable named maxIndex holds the index of the largest score.

Show Solution


10.4 -- Sorting an array using selection sort
Index
10.2 -- Arrays (Part II)

432 comments to 10.3 — Arrays and loops

  • Rwondo bislungo

    My solution for question #2:

    How's that?

  • frog

    My solution for Quiz #1 and #2

  • UnknownLearner

    My sample for Quiz 2

  • jatrindo

    Hey Alex, nascardriver,

    I think there's a small omission in the solution for question 3.

    In the question the declaration for the scores array is:

    But in the solution, the constexpr modifier disappears:

    Is this intended? Either way it works so it's not a huge issue.

  • Cesar

    Does my answer count as a answer for question number 2?

  • Jackson

    Bug in the 3rd Question hidded Solution

    This line below uses number 1 as start value for the loop, but array start from [0]. So the first value 84 not checked.
    Common boys, you already mentioned this bug under your own training:)

    for (int student{ 1 }; student < std::size(scores); ++student)

    Update: The question correct just the Hide Solution have this bug

    • Haldhar Patel

      You really need to check by yourself first if the program is running fine by running in your ide first then complain.

      The solution is absolutely fine, it first checks for index '0' indeed.
      Notice this line:

      here maxIndex is '0' and student is '1' so it is equivalent to:

      so you can see that scores[0] is checked then we move further..
      Hope it clears everything.

  • Josh

    Hi, in question 1 is there any reason the array length is stored in a variable? Is there anything wrong with using std::size(array) directly in the for loop condition?

    • Alex

      Before std::size existed, we had to use the array sizing trick to get the size of a fixed array (sizeof(array) / sizeof(array[0])). It was clearer to store the results of that calculation in a variable for documentary purposes.

      Now that std::size exists (in C++17), we can use that in place. I've updated the quiz answers.

  • Rayyan Khan

    I did Q2 without two for loops, hmmmm.

  • narf

    Question #1, line 38:

    It appears just a minor typo has occurred with

    being used instead of

  • Jamie

    You can also iterate through relevant segments of an array to achieve a desired effect while reducing the computing intensity compared to iterating through the entire array. Here's a couple applied examples (I also included a few formatting things for reasons):

    • Jamie

      #include<string>
      #include<iostream>

      //I reformatted this one in VS
      //with any luck, it won't wrap around this time

      struct Player {
          int spawnPos{};          //spawn position in level
          int pos{};               //current position in level
          int width{ 3 };          //width, used for collisions
          int collisionCount{ 1 }; // collision tally; resets to 0 before the game calculates collisions again
      };

      enum class tileType {
          pit,
          platform
      };

      void movePlayer(Player p) {
          //increase or decrease player.pos by 1 when the A or D key is hit
          //i.e. move the player left or right
      }

      void doCollision(Player p, tileType t) {
          p.collisionCount += (t == tileType::platform);
      }

      int main() {

          //for simplicity's sake, this is a 1-dimensional level. The same concepts apply for higher-
          //dimensional arrays, and become exponentially more necessary as the dimension count increases.

          const int levelSize{ 200 };
          tileType level1D[levelSize]{ tileType::pit }; //explicitly mark the default value in level1D[0]

          //separate inner parentheses from outer ones to make nesting easier to read
          Player player{ (levelSize - 1) / 2 }; //player spawns at the (roughly) halfway point in the level

          int spawnPlatSize{ 9 }; //size of the spawn platform

          //indentation works better because of these /*---*/ spacer comments
          for(int i = player.spawnPos - (spawnPlatSize / 2);  // left edge of the spawn platform
          /*---*/ i <= player.spawnPos + (spawnPlatSize / 2); //right edge of the spawn platform
          /*---*/ i++) {                                      //only the necessary segment is iterated through
              level1D[i] = tileType::platform;
          }

          player.pos = player.spawnPos;

          while (player.collisionCount > 0) {
              movePlayer(player);

              player.collisionCount = 0;

              //player is 3 tiles wide, so if they're 1 space away from a platform,
              //they should count as standing on it
              for(int i = player.pos - (player.width / 2);  //tile to the player's left
              /*---*/ i <= player.pos + (player.width / 2); //tile to the player's right
              /*---*/ i++) {                                //only the necessary segment is iterated through
                  tileType t{ level1D[i] };
                  doCollision(player, t);
              }

              //end while
          }

          std::cout << "Fell in a pit";

          //in the creation of the platform we save:
              //levelSize (200) - spawnPlatSize (9) = 191 of 200 max iterations = 95.5%

          //each time we run the while loop, we save:
              //levelSize (200) - player.width (3) = 197 of 200 max iterations = 98.5%

          //in a similar 2D level, we'd save:
              //(200^2)-(9^2) = 39,919 of 40,000 max iterations = 99.80%, and
              //(200^2)-(3^2) = 39,991 of 40,000 max iterations = 99.98%,
          //respectively.

          return 0;
      }

    • Jamie

      Edit buttons have spoiled me.

  • Mohammad Ali

    Hi,

    For question 2, it should use

    to discard anything in the input buffer instead of

    being used now.

  • hektik

    The trick to making the code this compact is in the logic performed in the while() statement.

  • Newguy01

    First Time i tried advancing code on myself and im proud of it :D
    Still would like to have some feedback, because you said if we pass an array to a function we really pass it and not just copy pass it, but if i dont change anything in the function at the array it should be fine right? like in my code.
    thank you very much for this guide :)

    • nascardriver

      `scores` and `numStuds` should be local variables of `main()`.
      If your functions don't modify the array they get via a parameter, the parameter should be `const`. Otherwise the functions won't be usable with `const` arrays and the caller has to assume that you're modifying the array.

  • NAVID

    • hektik

  • NAVID

  • Burfed

    As of 30th of January, VS2019 Community compiler throws a compile-time error when accessing invalid indexer that says "something something stack corruption". I had a question in mind, that I suppose will be answered later in the course: if I have declared and zero-initialized, for example, two arrays int[10], and try to access 10th or -1st indexer in any of them, is there a chance that a value in "neighbour" array gets changed?
    P.S. Thank you in advance!
    P.P.S. Sorry for my poor grammar, english is not my first language

  • Patrick

    Does std::size() return an unsigned int? Why is this? - Is it because sizes should always be positive?

    Also, I've seen some people use size_t count{} instead of just int count{} within for loops. Is there a reason you would want an unsigned counter instead of a signed one?

    • nascardriver

      Sizes are unsigned. You're losing range if you use a signed integer to store a size. C++ has `std::size_t` for sizes of any kind. Learncpp uses signed sizes because they're easier to use in certain cases. Using `std::size_t` saves you from needing casts all over the place.

  • J34NP3T3R

    in QUESTION #2 solution we may be able to do a SEARCH while PRINTING so that we only loop the array once.

    #include <iostream>
    #include <iterator> // for std::size

    int main()
    {
        // First, read in valid input from user
        int number{};
        do
        {
            std::cout << "Enter a number between 1 and 9: ";
            std::cin >> number;

            // if the user entered an invalid character
            if (std::cin.fail())
                std::cin.clear(); // reset any error flags

            std::cin.ignore(32767, '\n'); // ignore any extra characters in the input buffer (regardless of whether we had an error or not)

        } while (number < 1 || number > 9);

        // Next, print the array
        constexpr int array[]{ 4, 6, 7, 3, 8, 2, 1, 9, 5 };
        constexpr int arrayLength{ static_cast<int>(std::size(array)) };

       int matchIndex{-1};         // -1 because 0 is a valid index and -1 is not. so -1 means have not found yet
       for (int index{ 0 }; index < arrayLength; ++index)
        {
            std::cout << array[index] << ' ';
            if (array[index] == number)
            matchIndex = index ;
       }

       std::cout <<  "\n The number " << number << " has index " <<  index << "\n"; // assuming number exist in the list otherwise check if not -1 before printing.

        return 0;
    }

  • J34NP3T3R

    in the "Loops and arrays" example:
      constexpr int numStudents{ static_cast<int>(std::size(scores)) };

    BUT in Question #1 Solution:
      constexpr int arrayLength{ std::size(array) };

    why in Question #1 solution std::size did not have to be cast to int ?

    also is it OK to use int arrayLength{ std::size(array) }; or should it really be constexpr int arrayLength{ std::size(array) }; ?

  • kio

    Hi Nascardrive and Alex,

    I have done basically the same code as yours, expect in Q2 I did the following:

    Your while condition is better :)

  • Understood, will do. Thanks Alex!

    Also, my last if statement should have been:

  • I find "goto statements" are very easy to implement in situations like Questions 2. Is it fine to use them as long as the "goto point" is in sight of the "goto statement"? Like 5 lines or so?

  • Killed Question 1! Thank you!

  • aadil

    I feel like I've cheated for question 3
    I've written;

    thank you for your website, it is very useful

  • yeokaiwei

    Quiz 3
    Feedback. I got confused with the question for maxIndex.

    Due to the enum() examples, I thought you wanted us to add a variable to the end of the array called maxIndex e.g.  int scores[]{ 84, 92, 76, 81, 56 ,maxIndex};

  • yeokaiwei

    Quiz 1,2

    Interesting, you can't just
    a. use sizeof() in the loop E.g. for (int i = 0; i < sizeof(array) ; i++)
    b. use intialize an integer using sizeof() E.g. for (int i = 0; i < x ; i++)
    c  use any integer bigger than size of the array. E.g. 10,20

    You will get weird outputs.

    You must use    

  • Hey Guys,

    Not sure how to reach you otherwise, but, there is an app in Windows Store called Master C++ which is a copy-paste of this website.

    Just FYI.

    Thx for your grate job.

  • CC

    In your solution to Q2, when running the compiled program in a shell, pressing CTRL-D (which I believe sends an EOF character) results in the program running an ostensibly infinite loop. (In my terminal, it just prints

    over and over again.

    Do you know why this is happening, and how to write a program that is robust to this input? Thanks.

    • nascardriver

      Supposedly `std::cin` can be reused after receiving an EOF, but I couldn't get it to work.
      I don't think it's a good idea to reuse the stream after it reached EOF. Instead, I'd stop the program. You can check if EOF was found by calling `std::cin.eof()` and then returning from the loop.

      If you figure out how to get `std::cin` to work again after reading an EOF, please let me know. I suppose some terminals might close stdin after sending EOF, which would make it impossible to reset `std::cin`.

  • JBR

    When I used the solution code for obtaining the user's input(it is technically my attempt but I don't see the difference except that I used an additional function to obtain the user's valid entry and returned it to main), I needed to add the or condition with std::cin.fail(). Otherwise on entering an invalid decimal value (for ex 11.5), the prompt to enter the number was printing twice. On entering any other invalid character such as 'p', it was printing the prompt only once (as expected). My initial understanding was that the code was running the std::cin.clear() for both float 11.5 and int 11. But when I output the variable 'number' post extraction but before the std::cin.fail() statement, it prints "11Enter an integer between 1 and 9" once and then "0Enter an integer between 1 and 9". I don't understand what's causing the while loop to be run through twice for the invalid decimal input case. So basically, I figured a work-around but I didn't figure the problem out.

    PS:I'm a total beginner at C++, and coding in general, so I might be missing something obvious. Which is exactly what I want to know.

    • nascardriver

      If you enter "11.5", the "11" gets extracted and extraction stops at ".", because "." is not a valid character for integer extraction. Your loop loops, because line 14 is true. ".5" is left in the input buffer. Extraction fails, because there is no valid integer in the input. You remove ".5" from the input by using `ignore`. Line 14 loops again. The input buffer is now empty and you're prompted again in line 7.

      • JBR

        Oh that makes a lot of sense. So when i enter something like 4.5, the 4 gets extracted but the while loop is exited. Thanks for the clarification. Thanks also for the website and keep up the great work!

  • My solution to Question 2

    • nascardriver

      Line 12 and 15 are unconditional. They should be outside of the `if`.
      The 9 in line 23 doesn't do anything apart from confusing the caller of your function. `array` can have any size.
      Otherwise good job :)

Leave a Comment

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