Coming Soon

This lesson is currently being developed

Arrays and loops

Iterate through container elements effectively.

Dynamic arrays: std::vector
Chapter
Beginner
Difficulty
40min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

16.6 — Arrays and loops

In this lesson, you'll master the art of combining arrays (both C-style arrays and std::vector) with loops. You'll learn various loop patterns, when to use each type, and how to avoid common pitfalls when iterating through container elements.

Why arrays and loops go together

Arrays and loops are natural partners in programming:

  • Arrays store multiple related values
  • Loops provide a way to process each value systematically
  • Together, they enable powerful data processing patterns

Most real-world programming involves processing collections of data, making this one of the most important skills to master.

Loop types for array iteration

1. Index-based loops (traditional for loop)

The classic approach uses indices to access array elements:

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> scores = {85, 92, 78, 90, 88, 95, 82};
    
    std::cout << "Student scores:" << std::endl;
    
    // Index-based loop
    for (std::size_t i = 0; i < scores.size(); ++i)
    {
        std::cout << "Student " << (i + 1) << ": " << scores[i] << std::endl;
    }
    
    return 0;
}

Output:

Student scores:
Student 1: 85
Student 2: 92
Student 3: 78
Student 4: 90
Student 5: 88
Student 6: 95
Student 7: 82

Advantages of index-based loops:

  • You have access to the index (position) of each element
  • You can modify elements through the index
  • You can easily skip elements or process in reverse order

2. Range-based for loops (C++11)

The modern, cleaner approach:

#include <vector>
#include <iostream>

int main()
{
    std::vector<std::string> fruits = {"apple", "banana", "orange", "grape"};
    
    std::cout << "Available fruits:" << std::endl;
    
    // Range-based for loop (read-only)
    for (const std::string& fruit : fruits)
    {
        std::cout << "- " << fruit << std::endl;
    }
    
    return 0;
}

Output:

Available fruits:
- apple
- banana
- orange
- grape

Advantages of range-based loops:

  • Cleaner, more readable syntax
  • No risk of index out-of-bounds errors
  • Works with any container type
  • Less prone to off-by-one errors

3. Iterator-based loops

More flexible but slightly more complex:

#include <vector>
#include <iostream>

int main()
{
    std::vector<double> prices = {12.99, 8.50, 15.75, 22.00};
    
    std::cout << "Product prices:" << std::endl;
    
    // Iterator-based loop
    for (auto it = prices.begin(); it != prices.end(); ++it)
    {
        std::cout << "$" << *it << std::endl;
    }
    
    return 0;
}

Output:

Product prices:
$12.99
$8.5
$15.75
$22

Modifying elements during iteration

Modifying with index-based loops

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    std::cout << "Original: ";
    for (int num : numbers) std::cout << num << " ";
    std::cout << std::endl;
    
    // Double each number using index
    for (std::size_t i = 0; i < numbers.size(); ++i)
    {
        numbers[i] *= 2;
    }
    
    std::cout << "Doubled: ";
    for (int num : numbers) std::cout << num << " ";
    std::cout << std::endl;
    
    return 0;
}

Output:

Original: 1 2 3 4 5 
Doubled: 2 4 6 8 10 

Modifying with range-based loops

Use references to modify elements:

#include <vector>
#include <iostream>

int main()
{
    std::vector<std::string> words = {"hello", "world", "cpp", "programming"};
    
    std::cout << "Original: ";
    for (const std::string& word : words) std::cout << word << " ";
    std::cout << std::endl;
    
    // Convert to uppercase using range-based loop with references
    for (std::string& word : words) // Note: non-const reference
    {
        for (char& c : word) // Nested loop to process each character
        {
            c = std::toupper(c);
        }
    }
    
    std::cout << "Uppercase: ";
    for (const std::string& word : words) std::cout << word << " ";
    std::cout << std::endl;
    
    return 0;
}

Output:

Original: hello world cpp programming 
Uppercase: HELLO WORLD CPP PROGRAMMING 

Common loop patterns

1. Finding elements

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {10, 25, 30, 45, 50, 35, 20};
    int target = 30;
    
    // Find using index-based loop
    bool found = false;
    std::size_t foundIndex = 0;
    
    for (std::size_t i = 0; i < numbers.size(); ++i)
    {
        if (numbers[i] == target)
        {
            found = true;
            foundIndex = i;
            break; // Stop searching once found
        }
    }
    
    if (found)
    {
        std::cout << "Found " << target << " at index " << foundIndex << std::endl;
    }
    else
    {
        std::cout << target << " not found" << std::endl;
    }
    
    return 0;
}

Output:

Found 30 at index 2

2. Counting elements

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> grades = {85, 92, 78, 90, 88, 95, 82, 96, 89, 91};
    
    // Count grades above 90
    int countAbove90 = 0;
    for (int grade : grades)
    {
        if (grade > 90)
        {
            ++countAbove90;
        }
    }
    
    std::cout << "Total grades: " << grades.size() << std::endl;
    std::cout << "Grades above 90: " << countAbove90 << std::endl;
    std::cout << "Percentage above 90: " 
              << (100.0 * countAbove90 / grades.size()) << "%" << std::endl;
    
    return 0;
}

Output:

Total grades: 10
Grades above 90: 4
Percentage above 90: 40%

3. Calculating aggregates

#include <vector>
#include <iostream>

int main()
{
    std::vector<double> temperatures = {23.5, 25.2, 22.8, 24.1, 26.0, 21.5, 23.9};
    
    // Calculate sum, min, max, and average
    double sum = 0.0;
    double min = temperatures[0];
    double max = temperatures[0];
    
    for (double temp : temperatures)
    {
        sum += temp;
        if (temp < min) min = temp;
        if (temp > max) max = temp;
    }
    
    double average = sum / temperatures.size();
    
    std::cout << "Temperature Statistics:" << std::endl;
    std::cout << "Count: " << temperatures.size() << std::endl;
    std::cout << "Sum: " << sum << "°C" << std::endl;
    std::cout << "Average: " << average << "°C" << std::endl;
    std::cout << "Min: " << min << "°C" << std::endl;
    std::cout << "Max: " << max << "°C" << std::endl;
    
    return 0;
}

Output:

Temperature Statistics:
Count: 7
Sum: 167°C
Average: 23.8571°C
Min: 21.5°C
Max: 26°C

4. Transforming data

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> celsius = {0, 10, 20, 30, 40, 100};
    std::vector<double> fahrenheit;
    
    // Convert Celsius to Fahrenheit
    for (int c : celsius)
    {
        double f = (c * 9.0 / 5.0) + 32.0;
        fahrenheit.push_back(f);
    }
    
    std::cout << "Temperature conversions:" << std::endl;
    for (std::size_t i = 0; i < celsius.size(); ++i)
    {
        std::cout << celsius[i] << "°C = " << fahrenheit[i] << "°F" << std::endl;
    }
    
    return 0;
}

Output:

Temperature conversions:
0°C = 32°F
10°C = 50°F
20°C = 68°F
30°C = 86°F
40°C = 104°F
100°C = 212°F

Working with multi-dimensional arrays

Vectors of vectors (2D arrays)

#include <vector>
#include <iostream>

int main()
{
    // Create a 3x4 matrix (3 rows, 4 columns)
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    std::cout << "Matrix contents:" << std::endl;
    
    // Nested loops for 2D array
    for (std::size_t row = 0; row < matrix.size(); ++row)
    {
        for (std::size_t col = 0; col < matrix[row].size(); ++col)
        {
            std::cout << matrix[row][col] << "\t";
        }
        std::cout << std::endl;
    }
    
    // Alternative using range-based loops
    std::cout << "\nUsing range-based loops:" << std::endl;
    for (const auto& row : matrix)
    {
        for (int value : row)
        {
            std::cout << value << "\t";
        }
        std::cout << std::endl;
    }
    
    return 0;
}

Output:

Matrix contents:
1	2	3	4	
5	6	7	8	
9	10	11	12	

Using range-based loops:
1	2	3	4	
5	6	7	8	
9	10	11	12	

Reverse iteration

Reverse index-based loops

#include <vector>
#include <iostream>

int main()
{
    std::vector<std::string> words = {"first", "second", "third", "fourth", "last"};
    
    std::cout << "Forward order: ";
    for (const std::string& word : words)
    {
        std::cout << word << " ";
    }
    std::cout << std::endl;
    
    std::cout << "Reverse order: ";
    // Safe reverse iteration using signed integer
    for (int i = static_cast<int>(words.size()) - 1; i >= 0; --i)
    {
        std::cout << words[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Forward order: first second third fourth last 
Reverse order: last fourth third second first 

Using reverse iterators

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {10, 20, 30, 40, 50};
    
    std::cout << "Forward: ";
    for (auto it = numbers.begin(); it != numbers.end(); ++it)
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    std::cout << "Reverse: ";
    for (auto it = numbers.rbegin(); it != numbers.rend(); ++it)
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Forward: 10 20 30 40 50 
Reverse: 50 40 30 20 10 

Filtering and conditional processing

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> ages = {15, 22, 17, 35, 28, 19, 42, 16, 25, 33};
    
    // Filter into separate categories
    std::vector<int> minors;
    std::vector<int> adults;
    std::vector<int> seniors;
    
    for (int age : ages)
    {
        if (age < 18)
        {
            minors.push_back(age);
        }
        else if (age < 65)
        {
            adults.push_back(age);
        }
        else
        {
            seniors.push_back(age);
        }
    }
    
    std::cout << "Minors (" << minors.size() << "): ";
    for (int age : minors) std::cout << age << " ";
    std::cout << std::endl;
    
    std::cout << "Adults (" << adults.size() << "): ";
    for (int age : adults) std::cout << age << " ";
    std::cout << std::endl;
    
    std::cout << "Seniors (" << seniors.size() << "): ";
    for (int age : seniors) std::cout << age << " ";
    std::cout << std::endl;
    
    return 0;
}

Output:

Minors (3): 15 17 16 
Adults (7): 22 35 28 19 42 25 33 
Seniors (0): 

Performance considerations

Prefer range-based loops for simple iteration

#include <vector>
#include <iostream>
#include <chrono>

int main()
{
    std::vector<int> largeVector(1000000, 42);
    
    // Time index-based loop
    auto start = std::chrono::high_resolution_clock::now();
    long long sum1 = 0;
    for (std::size_t i = 0; i < largeVector.size(); ++i)
    {
        sum1 += largeVector[i];
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto indexTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    // Time range-based loop
    start = std::chrono::high_resolution_clock::now();
    long long sum2 = 0;
    for (int value : largeVector)
    {
        sum2 += value;
    }
    end = std::chrono::high_resolution_clock::now();
    auto rangeTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "Index-based loop time: " << indexTime.count() << " microseconds" << std::endl;
    std::cout << "Range-based loop time: " << rangeTime.count() << " microseconds" << std::endl;
    std::cout << "Both sums equal: " << (sum1 == sum2 ? "Yes" : "No") << std::endl;
    
    return 0;
}

Output (example):

Index-based loop time: 1234 microseconds
Range-based loop time: 1198 microseconds
Both sums equal: Yes

Common mistakes and how to avoid them

1. Off-by-one errors

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {10, 20, 30, 40, 50};
    
    // ❌ Wrong: Off-by-one error
    std::cout << "Wrong way (causes undefined behavior):" << std::endl;
    // for (std::size_t i = 0; i <= numbers.size(); ++i) // Don't do this!
    // {
    //     std::cout << numbers[i] << " "; // Will access out-of-bounds
    // }
    
    // ✅ Correct: Use < instead of <=
    std::cout << "Correct way:" << std::endl;
    for (std::size_t i = 0; i < numbers.size(); ++i)
    {
        std::cout << numbers[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Wrong way (causes undefined behavior):
Correct way:
10 20 30 40 50 

2. Modifying container size during iteration

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // ❌ Dangerous: Modifying size during iteration
    std::cout << "Dangerous approach (don't do this):" << std::endl;
    // for (std::size_t i = 0; i < numbers.size(); ++i)
    // {
    //     if (numbers[i] % 2 == 0)
    //     {
    //         numbers.erase(numbers.begin() + i); // Changes size and indices!
    //     }
    // }
    
    // ✅ Safe: Iterate backwards when removing elements
    std::cout << "Safe approach - iterate backwards:" << std::endl;
    for (int i = static_cast<int>(numbers.size()) - 1; i >= 0; --i)
    {
        if (numbers[i] % 2 == 0)
        {
            numbers.erase(numbers.begin() + i);
        }
    }
    
    std::cout << "Remaining numbers: ";
    for (int num : numbers) std::cout << num << " ";
    std::cout << std::endl;
    
    return 0;
}

Output:

Dangerous approach (don't do this):
Safe approach - iterate backwards:
Remaining numbers: 1 3 5 

3. Using wrong loop type for the task

#include <vector>
#include <iostream>

int main()
{
    std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
    
    // ❌ Overcomplicated: Using index when you don't need it
    std::cout << "Overcomplicated:" << std::endl;
    for (std::size_t i = 0; i < names.size(); ++i)
    {
        std::cout << names[i] << std::endl; // Index not actually needed
    }
    
    // ✅ Simple and clear: Range-based loop when you just need values
    std::cout << "Simple and clear:" << std::endl;
    for (const std::string& name : names)
    {
        std::cout << name << std::endl;
    }
    
    return 0;
}

Output:

Overcomplicated:
Alice
Bob
Charlie
Simple and clear:
Alice
Bob
Charlie

Summary

Loops and arrays are fundamental tools for processing collections of data:

  • Index-based loops: Use when you need the index or want to modify specific positions
  • Range-based loops: Use for simple iteration when you don't need indices
  • Iterator-based loops: Use for more complex navigation or when working with algorithms

Common patterns:

  • Finding elements with early termination (break)
  • Counting elements that meet criteria
  • Calculating aggregates (sum, min, max, average)
  • Transforming data from one form to another
  • Filtering elements into categories

Best practices:

  • Choose the right loop type for your task
  • Prefer range-based loops for simple iteration
  • Be careful with off-by-one errors
  • Don't modify container size during forward iteration
  • Use const references when you don't need to modify elements

In the next lesson, you'll learn about solving common challenges with array loops and handling edge cases.

Quiz

  1. When should you use an index-based loop instead of a range-based loop?
  2. How do you safely iterate backwards through a vector when removing elements?
  3. What's the most efficient way to iterate through a vector when you only need to read values?
  4. What happens if you use <= instead of < in an index-based loop condition?
  5. How do you modify vector elements using a range-based for loop?

Practice exercises

Try these exercises to practice arrays and loops:

  1. Statistics calculator: Write a program that calculates comprehensive statistics (count, sum, average, min, max, median) for a vector of test scores.

  2. Matrix operations: Create a 2D vector (matrix) and implement functions to find the sum of each row, each column, and the main diagonal.

  3. Data filter: Given a vector of integers, create separate vectors for positive numbers, negative numbers, and zeros. Count how many elements fall into each category.

  4. String processor: Given a vector of strings, find the longest string, count total characters, and create a new vector with all strings converted to uppercase.

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion