Coming Soon

This lesson is currently being developed

Array indexing and length using enumerators

Access container elements by index using enums.

Dynamic arrays: std::vector
Chapter
Beginner
Difficulty
35min
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.9 — Array indexing and length using enumerators

In this lesson, you'll learn how to use enumerators to make array indexing more meaningful, safe, and maintainable. Instead of using magic numbers for array indices, you'll discover how enums can make your code self-documenting and less prone to errors.

The problem with magic numbers

When working with arrays, it's common to use numeric indices that represent specific meanings:

#include <vector>
#include <iostream>

int main()
{
    // Student information: [name_length, age, grade_count, total_score]
    std::vector<int> student = {10, 20, 5, 450};
    
    // ❌ What do these numbers mean?
    std::cout << "Name length: " << student[0] << std::endl;
    std::cout << "Age: " << student[1] << std::endl;  
    std::cout << "Grade count: " << student[2] << std::endl;
    std::cout << "Total score: " << student[3] << std::endl;
    
    // ❌ Error-prone: easy to mix up indices
    double average = static_cast<double>(student[3]) / student[2]; // Which is which?
    std::cout << "Average: " << average << std::endl;
    
    return 0;
}

Output:

Name length: 10
Age: 20
Grade count: 5
Total score: 450
Average: 90

This code works, but it's hard to understand and maintain. What if someone adds a new field or reorders the data?

Introduction to enumerators for indexing

Enumerators can solve this problem by providing meaningful names for array indices:

#include <vector>
#include <iostream>

int main()
{
    // Define meaningful names for array indices
    enum StudentFields
    {
        NAME_LENGTH = 0,
        AGE = 1,
        GRADE_COUNT = 2,
        TOTAL_SCORE = 3,
        STUDENT_FIELDS_COUNT = 4 // Useful for array size
    };
    
    // Student information using the enum indices
    std::vector<int> student(STUDENT_FIELDS_COUNT);
    student[NAME_LENGTH] = 10;
    student[AGE] = 20;
    student[GRADE_COUNT] = 5;
    student[TOTAL_SCORE] = 450;
    
    // ✅ Much clearer and self-documenting
    std::cout << "Name length: " << student[NAME_LENGTH] << std::endl;
    std::cout << "Age: " << student[AGE] << std::endl;
    std::cout << "Grade count: " << student[GRADE_COUNT] << std::endl;
    std::cout << "Total score: " << student[TOTAL_SCORE] << std::endl;
    
    // ✅ Clear which values are being used
    double average = static_cast<double>(student[TOTAL_SCORE]) / student[GRADE_COUNT];
    std::cout << "Average: " << average << std::endl;
    
    return 0;
}

Output:

Name length: 10
Age: 20
Grade count: 5
Total score: 450
Average: 90

Practical examples

Example 1: Color representation (RGB)

#include <vector>
#include <iostream>

int main()
{
    // RGB color representation
    enum ColorComponents
    {
        RED = 0,
        GREEN = 1,
        BLUE = 2,
        COLOR_COMPONENT_COUNT = 3
    };
    
    // Create some colors
    std::vector<int> white(COLOR_COMPONENT_COUNT);
    white[RED] = 255;
    white[GREEN] = 255;
    white[BLUE] = 255;
    
    std::vector<int> purple(COLOR_COMPONENT_COUNT);
    purple[RED] = 128;
    purple[GREEN] = 0;
    purple[BLUE] = 128;
    
    // Function to display color
    auto displayColor = [](const std::vector<int>& color, const std::string& name)
    {
        std::cout << name << ": RGB(" 
                  << color[RED] << ", " 
                  << color[GREEN] << ", " 
                  << color[BLUE] << ")" << std::endl;
    };
    
    displayColor(white, "White");
    displayColor(purple, "Purple");
    
    // Calculate color intensity
    int whiteIntensity = white[RED] + white[GREEN] + white[BLUE];
    int purpleIntensity = purple[RED] + purple[GREEN] + purple[BLUE];
    
    std::cout << "White intensity: " << whiteIntensity << std::endl;
    std::cout << "Purple intensity: " << purpleIntensity << std::endl;
    
    return 0;
}

Output:

White: RGB(255, 255, 255)
Purple: RGB(128, 0, 128)
White intensity: 765
Purple intensity: 256

Example 2: Financial data

#include <vector>
#include <iostream>

int main()
{
    // Monthly financial data
    enum FinancialData
    {
        INCOME = 0,
        EXPENSES = 1,
        SAVINGS = 2,
        TAXES = 3,
        FINANCIAL_DATA_COUNT = 4
    };
    
    // Create financial records for 3 months
    std::vector<std::vector<double>> monthlyData = {
        {5000.0, 3500.0, 1000.0, 500.0}, // January
        {5200.0, 3600.0, 1100.0, 520.0}, // February  
        {4800.0, 3400.0, 900.0, 480.0}   // March
    };
    
    std::vector<std::string> months = {"January", "February", "March"};
    
    std::cout << "Financial Summary:" << std::endl;
    std::cout << "==================" << std::endl;
    
    double totalIncome = 0, totalExpenses = 0, totalSavings = 0, totalTaxes = 0;
    
    for (std::size_t month = 0; month < monthlyData.size(); ++month)
    {
        const auto& data = monthlyData[month];
        
        std::cout << months[month] << ":" << std::endl;
        std::cout << "  Income: $" << data[INCOME] << std::endl;
        std::cout << "  Expenses: $" << data[EXPENSES] << std::endl;
        std::cout << "  Savings: $" << data[SAVINGS] << std::endl;
        std::cout << "  Taxes: $" << data[TAXES] << std::endl;
        
        // Calculate net for the month
        double net = data[INCOME] - data[EXPENSES] - data[SAVINGS] - data[TAXES];
        std::cout << "  Net: $" << net << std::endl;
        std::cout << std::endl;
        
        // Add to totals
        totalIncome += data[INCOME];
        totalExpenses += data[EXPENSES];
        totalSavings += data[SAVINGS];
        totalTaxes += data[TAXES];
    }
    
    std::cout << "Quarterly Totals:" << std::endl;
    std::cout << "Total Income: $" << totalIncome << std::endl;
    std::cout << "Total Expenses: $" << totalExpenses << std::endl;
    std::cout << "Total Savings: $" << totalSavings << std::endl;
    std::cout << "Total Taxes: $" << totalTaxes << std::endl;
    
    return 0;
}

Output:

Financial Summary:
==================
January:
  Income: $5000
  Expenses: $3500
  Savings: $1000
  Taxes: $500
  Net: $0

February:
  Income: $5200
  Expenses: $3600
  Savings: $1100
  Taxes: $520
  Net: $-20

March:
  Income: $4800
  Expenses: $3400
  Savings: $900
  Taxes: $480
  Net: $20

Quarterly Totals:
Total Income: $15000
Total Expenses: $10500
Total Savings: $3000
Total Taxes: $1500

Using enumerators with 2D arrays

Enumerators are particularly useful with 2D arrays or matrices:

#include <vector>
#include <iostream>

int main()
{
    // Game board positions
    enum BoardRows
    {
        TOP_ROW = 0,
        MIDDLE_ROW = 1,
        BOTTOM_ROW = 2,
        BOARD_HEIGHT = 3
    };
    
    enum BoardCols
    {
        LEFT_COL = 0,
        CENTER_COL = 1,
        RIGHT_COL = 2,
        BOARD_WIDTH = 3
    };
    
    // Create a tic-tac-toe board
    std::vector<std::vector<char>> board(BOARD_HEIGHT, std::vector<char>(BOARD_WIDTH, ' '));
    
    // Place some moves
    board[TOP_ROW][LEFT_COL] = 'X';
    board[TOP_ROW][CENTER_COL] = 'O';
    board[MIDDLE_ROW][MIDDLE_ROW] = 'X';  // Center position
    board[BOTTOM_ROW][RIGHT_COL] = 'O';
    
    // Display the board
    std::cout << "Tic-Tac-Toe Board:" << std::endl;
    std::cout << "==================" << std::endl;
    
    for (int row = 0; row < BOARD_HEIGHT; ++row)
    {
        for (int col = 0; col < BOARD_WIDTH; ++col)
        {
            std::cout << " " << board[row][col] << " ";
            if (col < BOARD_WIDTH - 1) std::cout << "|";
        }
        std::cout << std::endl;
        
        if (row < BOARD_HEIGHT - 1)
        {
            std::cout << "---|---|---" << std::endl;
        }
    }
    
    // Check for winning conditions using meaningful names
    bool topRowWin = (board[TOP_ROW][LEFT_COL] == board[TOP_ROW][CENTER_COL] && 
                     board[TOP_ROW][CENTER_COL] == board[TOP_ROW][RIGHT_COL] &&
                     board[TOP_ROW][LEFT_COL] != ' ');
    
    bool diagonalWin = (board[TOP_ROW][LEFT_COL] == board[MIDDLE_ROW][CENTER_COL] &&
                       board[MIDDLE_ROW][CENTER_COL] == board[BOTTOM_ROW][RIGHT_COL] &&
                       board[TOP_ROW][LEFT_COL] != ' ');
    
    if (topRowWin)
    {
        std::cout << "Top row wins!" << std::endl;
    }
    else if (diagonalWin)
    {
        std::cout << "Diagonal wins!" << std::endl;
    }
    else
    {
        std::cout << "Game continues..." << std::endl;
    }
    
    return 0;
}

Output:

Tic-Tac-Toe Board:
==================
 X | O |   
---|---|---
   | X |   
---|---|---
   |   | O 

Game continues...

Scoped enumerations (enum class)

For better type safety, use enum class instead of plain enum:

#include <vector>
#include <iostream>

int main()
{
    // Scoped enumeration - better type safety
    enum class Coordinate
    {
        X = 0,
        Y = 1,
        Z = 2,
        DIMENSION_COUNT = 3
    };
    
    // Need to cast to use as array index
    std::vector<double> position(static_cast<int>(Coordinate::DIMENSION_COUNT));
    
    position[static_cast<int>(Coordinate::X)] = 10.5;
    position[static_cast<int>(Coordinate::Y)] = 20.3;
    position[static_cast<int>(Coordinate::Z)] = -5.7;
    
    std::cout << "Position: (" 
              << position[static_cast<int>(Coordinate::X)] << ", "
              << position[static_cast<int>(Coordinate::Y)] << ", "
              << position[static_cast<int>(Coordinate::Z)] << ")" << std::endl;
    
    // Helper function to make casting easier
    auto idx = [](Coordinate coord) { return static_cast<int>(coord); };
    
    std::cout << "Using helper: ("
              << position[idx(Coordinate::X)] << ", "
              << position[idx(Coordinate::Y)] << ", "
              << position[idx(Coordinate::Z)] << ")" << std::endl;
    
    return 0;
}

Output:

Position: (10.5, 20.3, -5.7)
Using helper: (10.5, 20.3, -5.7)

Creating utility functions with enumerators

#include <vector>
#include <iostream>
#include <string>

// Statistics array indices
enum class StatIndex
{
    COUNT = 0,
    SUM = 1,
    MIN = 2,
    MAX = 3,
    STAT_COUNT = 4
};

class StatisticsCalculator
{
private:
    static int idx(StatIndex stat) 
    { 
        return static_cast<int>(stat); 
    }
    
public:
    static std::vector<double> calculateStats(const std::vector<int>& data)
    {
        std::vector<double> stats(idx(StatIndex::STAT_COUNT));
        
        if (data.empty())
        {
            return stats; // All zeros
        }
        
        stats[idx(StatIndex::COUNT)] = data.size();
        stats[idx(StatIndex::SUM)] = 0;
        stats[idx(StatIndex::MIN)] = data[0];
        stats[idx(StatIndex::MAX)] = data[0];
        
        for (int value : data)
        {
            stats[idx(StatIndex::SUM)] += value;
            if (value < stats[idx(StatIndex::MIN)])
                stats[idx(StatIndex::MIN)] = value;
            if (value > stats[idx(StatIndex::MAX)])
                stats[idx(StatIndex::MAX)] = value;
        }
        
        return stats;
    }
    
    static void printStats(const std::vector<double>& stats)
    {
        std::cout << "Statistics:" << std::endl;
        std::cout << "  Count: " << stats[idx(StatIndex::COUNT)] << std::endl;
        std::cout << "  Sum: " << stats[idx(StatIndex::SUM)] << std::endl;
        std::cout << "  Min: " << stats[idx(StatIndex::MIN)] << std::endl;
        std::cout << "  Max: " << stats[idx(StatIndex::MAX)] << std::endl;
        std::cout << "  Average: " << stats[idx(StatIndex::SUM)] / stats[idx(StatIndex::COUNT)] << std::endl;
    }
};

int main()
{
    std::vector<int> testScores = {85, 92, 78, 90, 88, 95, 82, 87, 93, 89};
    
    auto stats = StatisticsCalculator::calculateStats(testScores);
    
    std::cout << "Test Scores: ";
    for (int score : testScores)
    {
        std::cout << score << " ";
    }
    std::cout << std::endl << std::endl;
    
    StatisticsCalculator::printStats(stats);
    
    return 0;
}

Output:

Test Scores: 85 92 78 90 88 95 82 87 93 89 

Statistics:
  Count: 10
  Sum: 879
  Min: 78
  Max: 95
  Average: 87.9

Best practices

1. Use meaningful names

// ✅ Good: Descriptive enum values
enum class PlayerStats
{
    HEALTH = 0,
    MANA = 1,
    EXPERIENCE = 2,
    STAT_COUNT = 3
};

// ❌ Bad: Generic or abbreviated names
enum Stats
{
    S1 = 0,
    S2 = 1,
    S3 = 2
};

2. Include a count value

// ✅ Good: Include count for array sizing
enum class WeekDays
{
    MONDAY = 0,
    TUESDAY = 1,
    WEDNESDAY = 2,
    THURSDAY = 3,
    FRIDAY = 4,
    SATURDAY = 5,
    SUNDAY = 6,
    DAY_COUNT = 7  // Useful for array sizing
};

std::vector<int> dailySteps(static_cast<int>(WeekDays::DAY_COUNT));

3. Consider using enum class for type safety

// ✅ Better: Scoped enumeration prevents accidental misuse
enum class Direction
{
    NORTH = 0,
    SOUTH = 1,
    EAST = 2,
    WEST = 3,
    DIRECTION_COUNT = 4
};

// Prevents accidental mixing with other enums
// Direction dir = Color::RED; // Compile error with enum class

4. Create helper functions for casting

// ✅ Helper function reduces casting noise
template<typename E>
constexpr auto idx(E enumerator) noexcept
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}

// Usage:
std::vector<int> data(idx(MyEnum::COUNT));
data[idx(MyEnum::FIRST_ITEM)] = 42;

Common pitfalls to avoid

1. Don't assume enum values

// ❌ Bad: Assuming enum values start at 0 and increment by 1
enum BadExample
{
    FIRST = 10,   // Not 0!
    SECOND = 20,  // Not 1!
    THIRD = 30    // Not 2!
};

// ✅ Good: Explicitly set values for array indexing
enum GoodExample
{
    FIRST = 0,
    SECOND = 1,
    THIRD = 2,
    COUNT = 3
};

2. Don't forget bounds checking

template<typename E>
bool isValidIndex(E enumValue, std::size_t arraySize)
{
    auto index = static_cast<std::size_t>(enumValue);
    return index < arraySize;
}

// Use before accessing:
if (isValidIndex(MyEnum::SOME_VALUE, myArray.size()))
{
    // Safe to access myArray[static_cast<int>(MyEnum::SOME_VALUE)]
}

Summary

Using enumerators for array indexing provides several benefits:

Advantages:

  • Self-documenting: Code becomes more readable and maintainable
  • Type safety: Prevents mixing up different types of indices
  • Error reduction: Less likely to use wrong magic numbers
  • Refactoring safety: Easy to reorder or add new fields

Best practices:

  • Use descriptive names for enum values
  • Include a COUNT value for array sizing
  • Consider enum class for better type safety
  • Create helper functions to reduce casting noise
  • Always validate indices when working with user input

When to use:

  • Arrays with fixed, meaningful structure
  • When indices represent specific concepts
  • Multi-dimensional arrays with logical dimensions
  • Data that benefits from named access patterns

In the next lesson, you'll learn about std::vector resizing and capacity management.

Quiz

  1. What advantages do enumerators provide over magic numbers for array indexing?
  2. Why is it useful to include a COUNT value in an enumeration used for indexing?
  3. What's the difference between enum and enum class when used for array indexing?
  4. How can you make casting enum values to integers more convenient?
  5. What should you consider when defining enum values for array indices?

Practice exercises

Try these exercises to practice using enumerators with arrays:

  1. Game character stats: Create an enumeration for RPG character statistics (health, mana, strength, dexterity, intelligence) and use it to manage a character's stats in a vector.

  2. Weekly planner: Create an enumeration for days of the week and use it to manage a weekly schedule stored in a vector. Include functions to add events, display the schedule, and find the busiest day.

  3. RGB color mixer: Create enumerations for RGB color components and use them to implement color mixing functions that can blend two colors or adjust brightness.

  4. Monthly budget tracker: Design an enumeration for budget categories (housing, food, transportation, entertainment, savings) and create a budget tracking system using vectors indexed by these enumerations.

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