Search

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


6.4 -- Sorting an array using selection sort
Index
6.2 -- Arrays (Part II)

361 comments to 6.3 — Arrays and loops

  • Joshua

    Q2 answer..... Well. I tried. Is recursion bad? I also did a lot more input checking than I think was intended. I also used cpp reference to find a way to look ahead in the buffer.

    • nascardriver

      > Is recursion bad?
      Yes, use a loop

      - You don't need `newLine` and shouldn't use 10. You can use '\n' directly.
      - Triple duplicate code in `validateUserInput`.
      - Use ++prefix unless you need postfix++. Postfix++ is slower.
      - Hardcoding `array` in `printArrow` and `printIndex` made those functions non-reusable. Pass the array as a parameter.

  • Swaraj

    std::size() works for me even when I don't include the iterator header file...why?(I use visual studio community 2019)

  • samira

    Question #2 solution

    • nascardriver

      You tell me what's wrong
      - Line 7, 13 (Lesson 4.12)
      - Line 27, 36 (Lesson 5.4)
      - `searchElement` (Lesson 0.11)
      If you can't figure it out, let me know and I'll tell you

  • samira

    Isn't this solution better in that there is one less iteration?

    • nascardriver

      Your idea only works if we know that `scores` has at least 1 element. For fixed-sized arrays, that's always the case, but we'll show other containers where this isn't true.
      Then there's always the question how far you'll take this. You could remove one more iteration if the array is large enough

      This game can be taken further and further. For that reason I usually keep it simple, even if it means looping once more.

      Anyway, good idea, works great for this quiz.

  • Innervate

    My answers for the quiz:
    Q 2:

    Q 3:

  • MIZZO

    I've seen how dumb i was after seeing the solution for quiz 3 lol :)
    anyways here is how i managed to solve it.

  • Eric

    part of my solution to quiz 2 is to use a variable like this:

    What's wrong with using a variable in this manner?

    • nascardriver

      There isn't always a value that you can use to indicate an error.
      You now have a magic number, -1. (That's not a huge deal, because -1 is a common error code, but should be avoided).
      You can read lesson 14.1 (Don't worry about the high number, you should be able to understand what's relevant to you), which covers a very similar problem, for a more detailed explanation.

      Rather than using a magic value, you can use `std::optional` from. It's not covered on learncpp, so here's a small example

  • Alek

    hello,here is my solution for question #2:
    [code]
    #include "Header.h"
    #include<iterator>
    #include <iostream>

    int main()
    {
        int array[]{ 4, 6, 7, 3, 8, 2, 1, 9, 5 };
        int numberOfElements{ static_cast<int>(std::size(array))};
        for (int number{ 0 }; number < numberOfElements; ++number) {
            if (number == numberOfElements - 1)
                std::cout << array[number]<<".\n";
            else
                std::cout << array[number] << " , ";
        }
        int inputNumber{};
        while (std::cout << "Enter a number between 1 to 9 :" &&
            (!(std::cin >> inputNumber)||(inputNumber>9))) {
            std::cin.clear();
            std::cin.ignore(32757, '\n');
            std::cout << "you have entered an invalid number please try again.\n";
        }
        std::cout << "Your inputNumber is correct! wait a moment to search the array please...\n";
        for(int number{0};number<numberOfElements;++number)
            if (inputNumber == array[number]) {
                std::cout << "the number is placed in index number \"" << number << "\"\n";
            }
        system("pause");
    }

    [\code]
    please look into the way I'm trying to validate user input.I asked you a question about it in last chapter if you remember it.is it a good way to do so ? I mean compared to cin.fail().I feel better about this one I used so wanna make sure both are equal and neat or not.
    also I would be thankful if you could tell me if my code is readable and I'm following best practices or not(since this is a small program I didn't add any comment to the code).
    thank you for your the effort you are putting into this website!

    • nascardriver

      Closing code tags use a forward slash (/).

      Your validation does unnecessary work (The check for `std::cout <<`, and clearing `std::cin` even if it's not erroneous) and is harder to understand. You noticed that yourself when you saw it on cppreference. There's nothing bad about using `std::cin >>` to get rid of the `std::cin.fail()` call, but don't use anything you don't need.

      - Don't use `std::system` if there's a way around it. It makes your code platform-dependent. If you're only using it for debugging, that's ok.

      2 issues that were also present in the lesson (Updated now), these are the lesson's fault, not yours:
      - Use `constexpr` for compile-time constants.
      - If a loop's body exceeds 1 line, wrap it in curly braces.

  • fTaras

    Is there a way to validate the integer user input, not the float one?

    • Innervate

      Maybe you can try a modulus conditional statement? something like if (Number % 1 != 0). Hopefully more knowledgeable can help.

  • Yolo

    In the examples above, why do we need to use static_cast<int> for numStudent? My compiler works just fine without it.

    • nascardriver

      This was previously asked by Robbas https://www.learncpp.com/cpp-tutorial/63-arrays-and-loops/comment-page-5/#comment-460412
      I still don't have a good reason to keep it, so I removed it from the lesson.

  • koe

    For quiz 2, you are already iterating through the array so no need to do it again (i.e. find the appearance index in the same loop).

  • Ahmed Alkanaq

    The solution to third quiz replaces the maxScore with maxIndex variable to hold the index of maximum variable.

    The maxScore, however, should be kept together with the maxIndex, otherwise there won't be any way to find out what the max score is in the first place...

    • nascardriver

      You can get the maximum score by accessing the element and the maximum index, have a look at the solution. If access to the array was expensive, your solution would be better.

  • Invino

    I can't for the life of me find why std::size does not work in my function findIndex() - Any pointers as to what I have done wrong here are appreciated. The code compiles and works correctly if I just execute everything in main().

    • nascardriver

      C-style arrays lose their size information when you pass them to functions, we talk about this later. You can add another parameter to `findIndex` to pass it the length, then call `std::size` in `main`.

      - Use ++prefix, you don't need postfix++. postfix++ is slower.
      - Enable compiler warnings, read them, fix them. `findIndex` is missing a `return`-statement (If the value isn't found).

  • Gabe

    Quiz 1 and Quiz 2

    Quiz 3

  • Robbas

    Hi Nascar and Alex. I was reviewing this lesson and I'd like a clarification about the std::size.

    Are we using the cast because std::size returns an unsigned value while we want a signed value, since we are using an int variable?

    Because, technically, for all the snippets in our cases this line is equivalent as a result to the one with the cast:

    So I just want to understand if this is the reason or there are other reasons for that.

    Thank you

    • nascardriver

      > Are we using the cast because std::size returns an unsigned value while we want a signed value, since we are using an int variable?
      Yes

      > for all the snippets in our cases this line is equivalent
      Yes that's right. I don't recall what made me add the casts. `std::size` runs at compile-time, which means the implicit conversion from unsigned to signed is allowed even in list initialization. I'll keep the casts for now. If I still can't remember why I added them the next time someone asks, I'll remove them.

  • Carl

    In question 3 the 'for' loop is initialised to 1. Since an array index starts at 0 (?) should it not be initialised to 0?

  • Carl

    My effort as follows. Nested the 'if' statement in main() for brevity. Any comments welcome.

  • Dudz

    My answer for question#3

    Sorry, I think overdid it. :)

  • Dudz

    Answer for question#1and2

  • Slyminc

    I dont know if it's only on me or it also happen on somebody else, but in my case std::size seemingly didn't really need <iterator> directive to run. and Yes, i'm running on c++17 with maximum warning and error levels. Any idea why this is working?

    • Alex

      One of the other headers you included probably included the iterator header. You shouldn't rely on this, as implementation may differ across compilers.

  • moji

    i'm using VSCode 2019 v6.1; my default cpp standard is set to C++20 & c standard to c11.
    but i cant use std::size(array) and it give me the following error:

    error: 'size' is not a member of 'std'
         int studentNum {static_cast<int>(std::size(score))};

    is there any config i missed??

    • nascardriver

      It doesn't matter which text editor you're using. You need to check which compiler you're using and look up if it supports C++17 (or C++20).

  • Matthias

    The solution to Quiz 1 is "wrong" in terms of it doesn't correspond to the array asked in the question.

  • BooGDaaN

    This is my solution for quiz 2. I hope this is ok.

  • gacrux

    On code blocks 17.12 (also tried the 20191006 nightly):

    ||=== Build: Debug in 63 lesson (compiler: GNU GCC Compiler) ===|
    C:\Users\admin\Documents\cbprojects\63 lesson\main.cpp||In function 'int main()':|
    C:\Users\admin\Documents\cbprojects\63 lesson\main.cpp|7|error: 'size' is not a member of 'std'|
    ||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

    https://en.cppreference.com/w/cpp/iterator/size
    Defined in header <iterator> (since C++17)

    Pretty sure the compiler is configured as shown in the very first lessons, already checked twice.

    Even the example in cppreference shows the same error :(

    • nascardriver

      https://www.learncpp.com/images/CppTutorial/Chapter0/CB-C++11-min.png
      This is the setting you need to look for, but you need at least C++17. If C++17 isn't available, you need to update GCC.

      • Gacrux

        The compiler is configured exactly as shown, but the error was still occurring.

        Updating to MinGW-W64 GCC-8.1.0 (x86_64-posix-seh) fixed the issue.

        For those interested: https://sourceforge.net/projects/mingw-w64/files/mingw-w64/

        The toolchain have to be configured manually in the compiler settings for the new binaries.

        • nascardriver

          Thanks for posting how you fixed it. I suppose you were using an old version of gcc and codeblocks didn't tell you that -std=c++17 was unsupported.

          • Chandler

            For some reason, C::B is still using an old compiler (v5.1.x) with their IDE. You'll definitely have to upgrade your compiler to have C++17 capabilities. I'm using Qt 5.13.2 and it ships with MinGW 7.3.0 and is 100% C++17 compliant.

  • Kim Stewart

    Write a program that reads 10 scores of a student, then find a average 10 scores, a number of passing score and a number of failing score.

    Assume that possible score is 100
    Passing score is 50 and above
    Failing score is 49 and below

  • Sam

    Here is my solution for Question #3

    • - Initialize your variables with brace initializers.
      - Compile-time constants should be `constexpr`.
      - Line 2: "C++14" :)

      • Sam

        fixed :)

Leave a Comment

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