2
#include <iostream>

int main()
{
   int arr[4]{0,1,2,3};
   int* begin = arr;
   int *end = &arr[4];
   int *firstElement = &arr[1];
   int *lastElement = &arr[3];
   int *onePastEnd = &arr[5];
   std::cout << firstElement - begin << std::endl; //outputs 1
   std::cout << lastElement - begin << std::endl; //outputs 3
   std::cout << end - begin << std::endl; //outputs 4
   std::cout << onePastEnd - begin << std::endl; //outputs 5
   //std::cout << (*(firstElement - begin)); //illegal indirection because you're trying to find out what is at an address, but this is not an address rather difference between two pointers
}

Is this a valid code (according to C++ standard) to point to locations past the last element?

Although the output seems valid, it is confusing because pointing out of range of array may result in an undefined behaviour right? Then how is this code working?

Edit: I am not asking about iterators but rather simple pointers, but the duplicate question is asking about iterators.

ZoomIn
  • 1,237
  • 1
  • 17
  • 35
  • 6
    It is valid to *point* to one element past the end of an array. It's invalid to *deference* that (non-existing) element. It's also invalid to *point* to **before** the array, or **more than one** after the end of the array. The variable named `onePastEnd` is actually **two** past end. – Eljay Aug 15 '22 at 18:00
  • 1
    It's legal to point **anywhere**. Accessing the value at the pointer is where things get nasty. – user4581301 Aug 15 '22 at 18:01
  • 1
    seems you misunderstood the "one past last element" thingy, or your indices are off. `int *end = &arr[4];` is already a pointer one past the last element. – 463035818_is_not_an_ai Aug 15 '22 at 18:05
  • Isn't end already pointing to one past last element while using end() with container classes? I just went with that logic. – ZoomIn Aug 15 '22 at 18:06
  • 1
    fwiw, `std::end(arr)` is the end iterator for `arr`, it points one past the last element, and because its a c-array, its just a pointer. – 463035818_is_not_an_ai Aug 15 '22 at 18:06
  • "Isn't end already pointing to one past last element while using end() with container classes?" exactly, and then you go one further with `onePastEnd` which is then two past the last element – 463035818_is_not_an_ai Aug 15 '22 at 18:07
  • 6
    @user4581301: No, it is not “legal” to point anywhere. The C++ standard defines pointer arithmetic only within arrays, including a position just beyond the end of the array and counting any object as an array of one element. For arithmetic outside of that, not only is the result of the arithmetic not defined, the entire behavior of the program that attempts it is not defined. – Eric Postpischil Aug 15 '22 at 18:08
  • @463035818_is_not_a_number I got what you mean. I was thinking 0, 1, 2, 3, end, onepastend ... – ZoomIn Aug 15 '22 at 18:09
  • then it was your notion of "one past last element" that is wrong. It says: "one past last element is ok" it does not say "one past the end, where end is one past the last element" – 463035818_is_not_an_ai Aug 15 '22 at 18:10
  • 1
    _"Then how is this code working?"_ Because Undefined Behavior does not mean that the compiler will infer what you want and then intentionally do something else. You are observing some _behavior_. And that behavior has not been defined by the language. – Drew Dormann Aug 15 '22 at 18:11
  • you also wrote it in the text "to point to locations past the last element?" the `end` iterator is not pointing to an element in the container – 463035818_is_not_an_ai Aug 15 '22 at 18:11
  • @463035818_is_not_a_number Fair enough. – ZoomIn Aug 15 '22 at 18:13
  • 1
    @ZoomIn Regarding your edit, pointers are iterators. They both have to behave the same way here. Also, both the Q and the top answers discuss this in terms of pointers. – NathanOliver Aug 15 '22 at 18:24
  • 1
    C++ won't hold your hand and lets you do many things, the fact that a program compiles and runs (or seems to run) is no guarantee it is correct. The code is "working" because you (accidentally) still point into memory in your readable datasegment. However that data is not part of your array. – Pepijn Kramer Aug 15 '22 at 18:32
  • @EricPostpischil And the rationale for arbitrary pointers being UB is somewhat obscure to the average PC user. IIUC, some architectures may trap already when certain addresses are loaded into address registers. Do you know any? – Peter - Reinstate Monica Aug 15 '22 at 18:33
  • @Peter-ReinstateMonica: I am not aware of hardware that traps based on addresses. However, there are several reasons not to define the behavior. C may be implemented with bank switching, with paging, and with supplementary data structures helping manage memory (e.g., implementing virtual memory manually in an embedded environment, with no OS support). Calculating addresses for some large block of allocated memory may require paging in appropriate information and loading data from memory just for the calculation… – Eric Postpischil Aug 15 '22 at 18:57
  • … Or a C implementation might provide bounds checking as a feature, so it will deliberately trap if an address calculation goes out of bounds. – Eric Postpischil Aug 15 '22 at 18:57
  • @Peter-ReinstateMonica: Additionally, limitations on pointer arithmetic can interact with optimization. When the compiler sees `int *p = &a[k];`, it can assume 0 ≤ k < n, where n is the number of elements in the array `a`, and then it can use that information in later code, such as `if` statements depending on conditionals involving `k`, without actually testing `k`. So, if `&a[k]` is outside the array, the optimizer’s conclusions about `k` might be wrong, and the program can go astray in any number of ways. – Eric Postpischil Aug 15 '22 at 19:29
  • 2
    @ZoomIn *Then how is this code working?* -- Think of it this way -- you buy a rope from a manufacturer that is guaranteed to hold 500 pounds. You tie 600 pounds to the rope, and the rope doesn't break. You buy the same rope again from the manufacturer, but the second rope breaks right away when you tie 600 pounds to it. Why did the first rope hold up, while the second broke right away? Who knows -- you broke the rules by tying 600 pounds to a rope that is guaranteed to hold 500 pounds. You went into "undefined behavior" when you did that. – PaulMcKenzie Aug 15 '22 at 19:48

0 Answers0