8

How do I iterate through a list of numbers, and how many different ways are there to do it?

What I thought would work:

#include <cstdlib>
#include <iostream>
#include <list>


using namespace std;


int main()

{
    int numbers[] = {2, 4, 6, 8};
    int i = 0;
    for(i=0; i< numbers.size();i++)
            cout << "the current number is " << numbers[i];


    system("pause");

    return 0;

}

I get an error on the for loop line:

request for member 'size' in 'numbers', which is of non-class type 'int[4]'

Vincent Savard
  • 34,979
  • 10
  • 68
  • 73
Dbz
  • 2,721
  • 4
  • 35
  • 53

9 Answers9

29

Unlike a lot of modern languages plain C++ arrays don't have a .size() function. You have a number of options to iterate through a list depending on the storage type.

Some common options for storage include:

// used for fixed size storage. Requires #include <array>
std::array<type, size> collection;

// used for dynamic sized storage. Requires #include <vector>
std::vector<type> collection;

// Dynamic storage. In general: slower iteration, faster insert
// Requires #include <list>     
std::list<type> collection;  

// Old style C arrays
int myarray[size]; 

Your options for iteration will depend on the type you're using. If you're using a plain old C array you can either store the size somewhere else or calculate the size of the array based on the size of it's types. Calculating the size of an array has a number of drawbacks outlined in this answer by DevSolar

// Store the value as a constant
int oldschool[10];
for(int i = 0; i < 10; ++i) {
    oldschool[i]; // Get
    oldschool[i] = 5; // Set
} 

// Calculate the size of the array
int size = sizeof(oldschool)/sizeof(int);
for(int i = 0; i < size; ++i) {
    oldschool[i]; // Get
    oldschool[i] = 5; // Set
}

If you're using any type that provides a .begin() and .end() function you can use those to get an iterator which is considered good style in C++ compared to index based iteration:

// Could also be an array, list, or anything with begin()/end()
std::vector<int> newschool;     

// Regular iterator, non-C++11
for(std::vector<int>::iterator num = newschool.begin(); num != newschool.end(); ++num) {
    int current = *num; // * gets the number out of the iterator
    *num = 5; // Sets the number.
}

// Better syntax, use auto! automatically gets the right iterator type (C++11)
for(auto num = newschool.begin(); num != newschool.end(); ++num) {
    int current = *num; // As above
    *num = 5;
}

// std::for_each also available
std::for_each(newschool.begin(), newschool.end(), function_taking_int);

// std::for_each with lambdas (C++11)
std::for_each(newschool.begin(), newschool.end(), [](int i) {
    // Just use i, can't modify though.
});

Vectors are also special because they are designed to be drop-in replacements for arrays. You can iterate over a vector exactly how you would over an array with a .size() function. However this is considered bad practice in C++ and you should prefer to use iterators where possible:

std::vector<int> badpractice;
for(int i = 0; i < badpractice.size(); ++i) {
    badpractice[i]; // Get
    badpractice[i] = 5; // Set
}

C++11 (the new standard) also brings the new and fancy range based for that should work on any type that provides a .begin() and .end(). However: Compiler support can vary for this feature. You can also use begin(type) and end(type) as an alternative.

std::array<int, 10> fancy;
for(int i : fancy) {
    // Just use i, can't modify though.
}

// begin/end requires #include <iterator> also included in most container headers.
for(auto num = std::begin(fancy); num != std::end(fancy); ++num) {
    int current = *num; // Get
    *num = 131; // Set
}

std::begin also has another interesting property: it works on raw arrays. This means you can use the same iteration semantics between arrays and non-arrays (you should still prefer standard types over raw arrays):

int raw[10];
for(auto num = std::begin(raw); num != std::end(raw); ++num) {
    int current = *num; // Get
    *num = 131; // Set
}

You also need to be careful if you want to delete items from a collection while in a loop because calling container.erase() makes all existing iterators invalid:

std::vector<int> numbers;
for(auto num = numbers.begin(); num != numbers.end(); /* Intentionally empty */) {
    ...

    if(someDeleteCondition) {
        num = numbers.erase(num);
    } else {
        // No deletition, no problem
        ++num;
    }
}

This list is far from comprehensive but as you can see there's a lot of ways of iterating over a collection. In general prefer iterators unless you have a good reason to do otherwise.

Community
  • 1
  • 1
Jake Woods
  • 1,808
  • 15
  • 20
6

Change you for loop to

 for(i=0; i< sizeof(numbers)/sizeof(int);i++){

In simple words, sizeof(numbers) mean number of elements in your array * size of primitive type int, so you divide by sizeof(int) to get the number of elements

Robin Chander
  • 7,225
  • 3
  • 28
  • 42
5

If you fix it so that it's list<int> numbers = {1,2,3,4}:

Iterating through using iterators:

#include <iterator>

for(auto it = std::begin(numbers); it != std::end(numbers); ++it) { ... }

Iterating through using std::for_each:

#include <algorithm>
#include <iterator>

std::for_each(numbers.begin(), numbers.end(), some_func);

Utilizing a for-each loop (C++11):

for(int i : numbers) { ... }
Yuushi
  • 25,132
  • 7
  • 63
  • 81
3

I didn't see it among the answers but this is imo the best way to do it: Range-based for loop

It is safe, and in fact, preferable in generic code, to use deduction to forwarding reference:
for (auto&& var : sequence).

Minimalist and working example :

#include <list>
#include <iostream>

int main()
{
    std::list<int> numbers = {2, 4, 6, 8};
    for (const int & num : numbers)
        std::cout << num << "  ";
    std::cout << '\n';
    return 0;
}
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
3

If your list of numbers is fixed be aware that you can simply write:

#include <iostream>
#include <initializer_list>

int main()
{
    for (int i : {2, 4, 6, 8})
        std::cout << i << std::endl;
    return 0;
}
kubus
  • 109
  • 4
2

There is no size function on "plain" C-style arrays. You need to use std::vector if you want to use size, or calculate size through sizeof.

In C++11 you can use array initialization syntax to initialize your vectors, like this:

vector<int> numbers = {2, 4, 6, 8};

Everything else stays the same (see demo here).

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
2

You can also use the plain old C containers and use the iterator syntax for the loop:

#include <iostream>

int main()
{
    int numbers[] = {2, 4, 6, 8};
    int *numbers_end = numbers + sizeof(numbers)/sizeof(numbers[0]);
    for (int *it = numbers; it != numbers_end; ++it)
        std::cout << "the current number is " << *it << std::endl;

    return 0;
}
user1346466
  • 630
  • 4
  • 11
0

There is no member function "size" because "numbers" isn't a class. You can not get the array's size this way, you must either know it (or compute it) or use some class to store your numbers.

KamikazeCZ
  • 714
  • 8
  • 22
0

The easiest way to do it, in my opinion, would be to use a span.

#include <cstdlib>
#include <iostream>
#include <gsl/span>

int main() {
    int numbers[] = {2, 4, 6, 8};
    for(auto& num : gsl::span(numbers)) {
            cout << "the current number is " << num;
    }

    system("pause");
}

Notes:

  • Spans are part of the GSL library. To use them, download the library from here, and add the download path to the compilation command, e.g. g++ -o foo foo.cpp -I/path/to/gsl
  • In C++20, span will be part of the standard, so you would just use std::span and #include <span>.
einpoklum
  • 118,144
  • 57
  • 340
  • 684