9

In C++ Primer, Chapter 2, "Variables and Basic Types", it says:

it is possible for a pointer to an object and a pointer one past the end of a different object to hold the same address.

I'm not a native speaker, and I think that's why I'm a bit confused by the sentence "a pointer one past the end of an object". Would anyone one tell me what it means please?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
libra
  • 673
  • 1
  • 10
  • 30

3 Answers3

13

It means that after

int a[] = { 1, 2 };
float b;

it's possible that (void *) &a[2] == (void *) &b may compare as true.

&a[2] (or equivalently, a+2) is a pointer just past the end of a, because the array only contains elements with indices 0 and 1.

Normally, out-of-range array indices are completely invalid (accessing a[2] is not allowed, nor is even computing &a[3]), but there is a special exception for computing the address just past the end of an array, because as it turned out, that is quite useful, for example when iterating over an array and you need an end value to know when to stop the loop.

  • thx man!, that's exactly what i want to know about, but can you explain a bit more on (void *)?, i dont quite get this part of the answer – libra Feb 18 '14 at 10:17
  • 5
    Formally, `&a[2]` is undefined behavior. The only legal way to get a pointer one past the end is `a + 2`. – James Kanze Feb 18 '14 at 10:20
  • @JamesKanze The intent is that it is defined behaviour. C has formally made it defined, and for C++, that intent has explicitly been stated even if the standard doesn't match the intent yet. –  Feb 18 '14 at 10:35
  • @libra Without a cast to `void *`, you've got a comparison between `int *` and `float *`. I could have used `int b;` as an example, in which case the cast would be unnecessary, but I wanted to highlight that it can happen regardless of type. –  Feb 18 '14 at 10:37
  • @hvd: That is correct. So, in conclusion, at the present time, _exactly what James said_. – Lightness Races in Orbit Feb 18 '14 at 10:59
  • @LightnessRacesinOrbit For a question with the language-lawyer tag, it would be entirely correct. For this question, it's technically correct, but misleading, in my opinion. –  Feb 18 '14 at 11:07
  • @hvd References, please. I know that at one time, the C++ committee explicitly decided _not_ to follow C in this (since they didn't want to support syntax for C style arrays which couldn't be emulated by `std::vector`). They may have changed their mind later, but I've not heard about it. – James Kanze Feb 18 '14 at 11:17
  • @hvd And it's only "sort of" language lawyering, since if the user later replaces `int a[]` with `std::vector a`, he _will_ get an exception if he does `&a[2]` (supposing 2 elements in the vector, and a debugging version of `std::vector`). This is why the C++ committee didn't adopt the wording from C standard, and intentionally kept it undefined behavior. – James Kanze Feb 18 '14 at 11:21
  • @JamesKanze My reference is [CWG issue 232](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232). But good point about container classes. –  Feb 18 '14 at 11:52
  • @hvd Interesting. One of my references would have been Tom Plum as well (but from a private conversation, about 10 years earlier). Maybe the committee did change its mind (although the current wording of the standard doesn't reflect it). (The issue came up before the adoption of the first standard, when it became clear that C was going to add such wording to the next version of their standard.) – James Kanze Feb 18 '14 at 14:44
  • Does `int* ptr = (int*) malloc(sizeof(int)), *ptr2 = ptr + 1;` invoke UB? What about the same in C? – Spikatrix Mar 21 '17 at 09:37
  • @CoolGuy In C, that is definitely valid. In C++, there is no object, and off of the top of my head I do not know if this makes it invalid: as it would not compute one-past-the-end-of-an-object, the exception might not apply. Will check in more detail when I can. –  Mar 21 '17 at 11:49
  • @CoolGuy After checking the standard, I still don't know. For more background: the problem is the same as how after `int* ptr = (int*) malloc(sizeof(int));`, `*ptr = 1;` is invalid, as it assigns to a non-existent object. C++ strictly speaking requires placement new there to create an object, after which the assignment would become valid, and after which `int* ptr2 = ptr + 1;` would become unambiguously valid as well. –  Mar 22 '17 at 19:17
13

Suppose you have an array, int foo[5] = {1,2,3,4,5};. It's laid out in memory like this:

-----------
|1|2|3|4|5|
-----------

It's legal to have a pointer that points to any member of the array; but it's also legal for a pointer to point one past the end of the array (usually to signal that it has reached the end of the array when doing an STL algorithm using iterators) - although it isn't legal to dereference that pointer. Like so:

-------------
|1|2|3|4|5|?|
-------------
 ^         ^
 |         |
 p         q

p is a pointer into the array; q is a pointer one-past-the-end.

Now, suppose you also have an array const char bar[3] = "Hi";. It's possible that the two arrays have been allocated next to each other in memory, like so:

<--foo---> <-bar->
-----------------
|1|2|3|4|5|H|i|0|
-----------------
 ^         ^
 |         |
 p         q

Then q is both a one-past-the-end for foo, and also pointing to the physical location for bar[0].

Chowlett
  • 45,935
  • 20
  • 116
  • 150
  • thank you so much, that's exactly what i want to know, – libra Feb 18 '14 at 10:11
  • one more question, how do i assign a pointer to the one-past-the-end of that array?, like this "int *pi = foo[6]" ? – libra Feb 18 '14 at 10:15
  • 2
    @libra: `int* pi = foo+6;` or `int* a = &foo[5], *pi = a+1;` [It is debatable as to whether `int* pi = &foo[6];`](http://stackoverflow.com/a/3144917/560648) [is legal/well-defined](http://stackoverflow.com/a/988254/560648). – Lightness Races in Orbit Feb 18 '14 at 10:17
2

"one past" means "+1 operation on the pointer". The following code generates:

0x7ffffcf6a848
0x7ffffcf6a84c
0x7ffffcf6a84c

As you can see, the address of "one past a", i.e., &a + 1 is the same as &b.

    int a = 2; 
    int b = 3;
    cout<< &a << endl << &a + 1 << endl << &b <<endl;
Peng Zhang
  • 3,475
  • 4
  • 33
  • 41
  • No it doesn't. It means "+1 operation on the pointer such that you are no longer within the bounds of the data it points to, but only by one element". – Lightness Races in Orbit Feb 18 '14 at 10:11
  • 1
    @LightnessRacesinOrbit The whole sentence in the question, i.e., "one past the end of ..." is what you said in the comment. I just answered the meaning of "one past". It literally means "+1 operation on pointer". :-) – Peng Zhang Feb 18 '14 at 10:14
  • No, again, it literally doesn't. "One past" means you are beyond the underlying object by one. Any arbitrary +1 operation is an _increment_; you could call it "one up" but nobody else does! `int ar[3] = {}; int* ptr = ar; ar+1; // NOT "one past"!` – Lightness Races in Orbit Feb 18 '14 at 10:26
  • Haha I was just listening to "Let It Go". Weird. – Lightness Races in Orbit Feb 18 '14 at 10:27
  • 1
    @LightnessRacesinOrbit That ONLY depends how you understand "past" in english. One day past 2/12/13. Therefore, in my view, you are just talking about English not programming in C++. – Peng Zhang Feb 18 '14 at 10:29
  • @LightnessRacesinOrbit ar+1 is one past the previous value of ar. Isn't it? This is just about ENGLISH. – Peng Zhang Feb 18 '14 at 10:30
  • I know it is about English, and as someone who has lived in _England_ for several decades I am sitting here telling you that your English is wrong: that is not the commonly accepted meaning of "one past" in this context. – Lightness Races in Orbit Feb 18 '14 at 10:32