Search code examples
c++c++11undefined-behaviorranged-loops

Is range-based for-loop to iterate over an char[] is undefined-behavior?


(assuming that I can not use STL container)

#include <iostream>

int main()
{
    wchar_t my_array[] = { L'h', L'e', L'l', L'l', L'o' };

    for (const auto& wch : my_array) {
        std::wcout << wch;
    }
}

The range-based for loop in C++ uses the begin() and end() functions to determine the range of elements to iterate over. In the case of an array, as above, std::begin(my_array) returns a pointer to the first element of the array, and std::end(my_array) returns a pointer to one past the last element of the array.

It works, but is it UB?


Solution

  • No. It is not undefined.

    A pointer can point one past the last element of an array.

    Actually std::vector iterators can be implemented as raw pointers. Also a pointer to an object can be regarded as pointer to a single element array. Incrementing such pointer once is legal. You just may not dereference it. The end pointer / iterator is not dereferenced in the ranged based for loop. Dereferencing the end iterator would be UB as well as dereferencing the pointer.

    int a[] = {1,2,3};
    int* a_end = a + 3;  // OK
    // *a_end ... NOT OK
    
    int x = 42;
    int p = &x;
    int* p_one_past = p+1;      // not that useful, but OK
    // *p_one_past ... NOT OK
    

    Also when using c-style arrays with algorithms that expect first and last iterators it is common to pass a one-past-last-element pointer:

    int a[] = {3,2,1};
    std::sort( std::begin(a) , std::end(a) );
    

    Here std::end(a) is the same as a + std::size(a) is the same as a + 3.