2

Here is the code

#include <stdio.h>

int a[] = {1, 2, 3, 4, 5};

int main()
{
    for (int i : a)
        printf("%d\n", i);
    return 0;
}

In my opinion, for (int i : a) only works when a is a vector of integers. But in above code, a is an array of integers which is much like a pointer to integer. Why does this work?

flexwang
  • 625
  • 6
  • 16
  • check this out: [range for](https://books.google.ca/books?id=kCF4BgAAQBAJ&pg=PA234&dq=straustrup+c%2B%2B+range+for&hl=en&sa=X&redir_esc=y#v=onepage&q=straustrup%20c%2B%2B%20range%20for&f=false) – StahlRat Mar 05 '16 at 03:11
  • 3
    This is a rather odd question. Taking a piece of code that performs perfectly, then assuming it shouldn't be performing perfectly due to an assumption. The answer to "why does it work" is obviously "the assumption is faulty", because the compiler always knows better than you! Why did you not instead investigate the faulty assumption? – Lightness Races in Orbit Mar 05 '16 at 14:39
  • Why shouldn't it work? Why would you want it to not work? – Rob K Mar 07 '16 at 15:33

6 Answers6

13

Array is not "much like a pointer". Arrays can decay to pointers, but in a non-decaying context, it's still an array with a known size.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
3

As mentioned in other answers, arrays are not pointers. They decay into pointers in some contexts, true, but only in some contexts.

int a[] = {1, 2, 3, 4, 5};

Type of a is int [5], an array with five elements of type int. In this case a pair of empty square brackets tell the compiler to deduce the number of elements, so you definition is equivalent to int a[5] = {1, 2, 3, 4, 5};.

If you are still not convinced, here is a piece of code that tries to make a compiler emit an error message containing the type of a:

template <typename T> struct Type;

int main()
{
    int a[] = {1, 2, 3, 4, 5};
    Type<decltype(a)> dummy;
}

g++ 5.3 emits

error: aggregate ‘Type<int [5]> dummy’ has incomplete type and cannot be defined

As you can see, decltype(a) is int [5].

cppreference.com contains a detailed explanation of range-based for loop. Your for loop is equivalent to:

{
    auto && __range = a; 
    for (auto __begin = a, __end = a + 5; __begin != __end; ++__begin)
    {
        int i = *__begin; 
        std::printf("%d\n", i);
    }
}

To summarize: T [N] is a type: an array with N elements of type T. N is an integral constant known at compile-time, so the compiler knows where the last element of the array is, and hence is able to generate a loop that iterates over all the elements.

If you want to know more about array decay, I suggest reading What is array decaying?.

Community
  • 1
  • 1
sawyer
  • 473
  • 4
  • 15
1

The compiler knows everything it needs to know about that array

T arr[]

is an incomplete type, but it gets better and "feature complete" with the part on the right which is a list, a list of defined size, known to the compiler at compile time so it is statically checked and there you have it, you know the size, you even know the elements; what more do you need ?

Array are not pointers by the way, to better understand the problem you can tackle this from different angles, for example you should understand that when you pass an array to a function you are actually solving a problem that can't be solved neatly in C and C++, there is not void foo( T arr [] ), but you are forced to use void foo ( T * arr ) because you can't create a real interface that is arrays-only in C/C++ .

xelp
  • 47
  • 4
  • 1
    "because you can't create a real interface that is arrays-only in C/C++ ." - technically correct because there is no such thing as "C/C++" . But you can create such an interface in C++. – M.M Mar 05 '16 at 04:01
  • 2
    but you can use `template void foo(T (&arr)[N]) {}` – NathanOliver Mar 05 '16 at 04:40
0

In my opinion, for (int i : a) only works when a is a vector of integers

You are wrong.

In above code, a is an array of integers which is much like a pointer to integer

You are wrong here, too. Arrays and pointers are two completely different things.

Why does this work?

Because that's what ranged-for does. It iterates over containers. An array is a container.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

It's because it calls std::begin() and std::end(), which specializes for C arrays. This is from glibc's <range_access.h>:

template<class _Tp, size_t _Nm>
inline _Tp*
begin(_Tp (&__arr)[_Nm])
{ return __arr; }

template<class _Tp, size_t _Nm>
inline _Tp*
end(_Tp (&__arr)[_Nm])
{ return __arr + _Nm; }
KevinZ
  • 3,036
  • 1
  • 18
  • 26
0

Because the compiler respect the C++ standard. Draft n4296 explicitely says that range-based for statement are valid for plain arrays:

6.5.4 The range-based for statement [stmt.ranged]

For a range-based for statement of the form

for ( for-range-declaration : expression ) statement

...
(1.1) — if _RangeT is an array type.... If _RangeT is an array of unknown size or an array of incomplete type, the program is ill-formed;

And int a[] = {1, 2, 3, 4, 5}; defines an array of 5 integers.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252