0

(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?

Amit
  • 645
  • 1
  • 3
  • 19
  • 4
    The semantics of **end** is one past the last element. It is not UB. (It'd be UB to dereference that bad location, though.) – Eljay Jan 24 '23 at 15:34
  • Definitely well defined as far as I know. – AnArrayOfFunctions Jan 24 '23 at 15:34
  • 2
    A pointer to one past the end of an array is not UB as long as you don't dereference it. – john Jan 24 '23 at 15:34
  • Side note that wchar_t is bit of a strange thing, it is only 16 bits on windows. You can also just use std::wstring in ths case. `std::wstring my_word{L"Hello"};` and then do your range based for over that. (I find that a lot less annoying to type then all the characters seperatly, and there is still short string optimization). – Pepijn Kramer Jan 24 '23 at 15:37
  • 4
    Ranges in C++ are always half-open; `std::end` is always one-past-the-end. Just like the `end()` member function on container classes. (Life would be pretty miserable if this didn't work the same with arrays as with other containers.) – molbdnilo Jan 24 '23 at 15:39
  • @PepijnKramer, I know that. See my 1st line: "(assuming that I can not use STL container)" – Amit Jan 24 '23 at 15:39
  • One past the last element of the array is **the cornerstone** of all C and C++. Asking whether it is UB is like asking whether 0 is UB. – n. m. could be an AI Jan 24 '23 at 15:43
  • The above is not UB, but you might be surprised to learn that the shown code outputs 6 characters, and not 5. – Sam Varshavchik Jan 24 '23 at 15:49
  • 1
    @SamVarshavchik Err, what?! It outputs 5 chars. [The loop body is executed 5 times.](https://godbolt.org/z/je9q55Wvs) – Konrad Rudolph Jan 24 '23 at 15:55
  • 1
    @SamVarshavchik No it does not. – n. m. could be an AI Jan 24 '23 at 16:00

3 Answers3

2

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

The situation you describe does not lead to undefined behaviour. Iterating over a char array is not much different than iterating any other array. Note that in c++ end is supposed to point to one element past the last element in the collection. This is not an exception in this case, but the norm and is also what for each loop in c++ expects.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
0

No this is not UB.

As a matter of fact it is quite similar to using a range-based for loop with an stl container like std::vector. The std::vector::end() iterator in practice also points to one element past the last element of the std::vector.

If you would dereference it, it would be UB, but this is not required for executing a ranged-based for loop.

wohlstad
  • 12,661
  • 10
  • 26
  • 39