Pointing one past the end element

I

Ioannis Vranos

Are the following codes valid?


1.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

ptrdiff_t d= &ai[10]- &ai[0];

}



2.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

int *p= &ai[10];

}


I thought both are valid, but I got confused by C++03 regarding vector:

"The elements of a vector are stored contiguously, meaning that if v is
a vector<T, Allocator> where T is some type other than bool, then it
obeys the identity &v[n] == &v[0] + n for all

===> 0 <= n < v.size()".

It says this instead of "0<= n <=v.size()".
 
T

Tadeusz B. Kopec

Are the following codes valid?


1.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

ptrdiff_t d= &ai[10]- &ai[0];

}

I don't know what you mean 'valid'. Undefined behaviour, no diagnostic
required.
2.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

int *p= &ai[10];

}

Again UB. But
int *p = ai + 10;
is OK.

The problem is, that you can create a pointer one past the last element,
but dereferencing it is UB. And operator[] dereferences.

--
Tadeusz B. Kopec ([email protected])
"Mach was the greatest intellectual fraud in the last ten years."
"What about X?"
"I said `intellectual'."
;login, 9/1990
 
V

Victor Bazarov

Ioannis said:
Are the following codes valid?
No.



1.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

ptrdiff_t d= &ai[10]- &ai[0];

}



2.

#include <cstddef>


int main()
{
using namespace std;

int ai[10];

int *p= &ai[10];

}


I thought both are valid, but I got confused by C++03 regarding
vector:
"The elements of a vector are stored contiguously, meaning that if v
is a vector<T, Allocator> where T is some type other than bool, then
it obeys the identity &v[n] == &v[0] + n for all

===> 0 <= n < v.size()".

It says this instead of "0<= n <=v.size()".

Your code involves dereferencing the "one-past-the-end" pointer,
and that's UB. The vector code involves calling the overloaded
op[] with index out of range, and that's UB according to the
definition of op[].

V
 
A

Andrew Koenig

Are the following codes valid?
int ai[10];

ptrdiff_t d= &ai[10]- &ai[0];

Not quite -- but the following, which looks almost the same, is valid:

ptrdiff_t d = (ai + 10) - &ai[0];
int ai[10];

int *p= &ai[10];

Again, this example is not valid, but the following, which looks almost the
same, is valid:

int *p = ai + 10;
"The elements of a vector are stored contiguously, meaning that if v is a
vector<T, Allocator> where T is some type other than bool, then it obeys
the identity &v[n] == &v[0] + n for all

===> 0 <= n < v.size()".

It says this instead of "0<= n <=v.size()".

Correct, because &v[n] is undefined.

The trouble is that once you have evaluated v[n], you have undefined
behavior, even if all you do with the result is take its address.

On the other hand, you can evaluate v.begin() + n, and as long as you don't
try to dereference it, you'll be fine.
 
J

Juha Nieminen

Ioannis said:
I thought both are valid, but I got confused by C++03 regarding vector:

What you have in your code is an array, not a vector, thus anything
the standard says about vectors doesn't (necessarily) apply to arrays.
 
T

Tomás Ó hÉilidhe

You're allowed have a pointer to one past the last element:

int arr[8];

int *p = arr;
int const *const pend = arr + 8;

do *p++ = 77;
while (pend != p);
 
V

Victor Bazarov

Tomás Ó hÉilidhe said:
You're allowed have a pointer to one past the last element:

int arr[8];

int *p = arr;
int const *const pend = arr + 8;

do *p++ = 77;
while (pend != p);

Absolutely. And you're not allowed to dereference it.

V
 
J

James Kanze

Are the following codes valid?

#include <cstddef>
int main()
{
using namespace std;
int ai[10];
ptrdiff_t d= &ai[10]- &ai[0];
}
2.

#include <cstddef>

int main()
{
using namespace std;
int ai[10];

int *p= &ai[10];
}

Good question. In C90, both result in undefined behavior. In
C99, other are well defined, because of a special rule (aka a
horrible hack) saying that the undefined behavior due to
dereferencing doesn't occur if the immediately following
operation is taking the address.

At least at present, C++ follows the C90 standard. (I believe
that there was some discussion about changing this, however. In
an even more general way than in C99: an lvalue expression which
refers to one past the end would be legal as long as there was
no lvalue to rvalue conversion, and no attempt to actually
modify the object through the expression. I don't know the
status of that discussion, however.)
I thought both are valid, but I got confused by C++03
regarding vector:
"The elements of a vector are stored contiguously, meaning
that if v is a vector<T, Allocator> where T is some type other
than bool, then it obeys the identity &v[n] == &v[0] + n for
all
===> 0 <= n < v.size()".
It says this instead of "0<= n <=v.size()".

That's for vector, not for an array. In the case of vector, of
course, it is undefined behavior (and many implementations will
core dump if you attempt to do it).
 
I

Ioannis Vranos

James said:
That's for vector, not for an array. In the case of vector, of
course, it is undefined behavior (and many implementations will
core dump if you attempt to do it).


I suppose a vector is usually implemented by allocating space in the
heap using operator new.
 
V

Victor Bazarov

Ioannis said:
I suppose a vector is usually implemented by allocating space in the
heap using operator new.

AFAIK, it's always implemented using the Allocator template argument
to do all memory allocations/deallocations.

V
 
I

Ioannis Vranos

Victor said:
AFAIK, it's always implemented using the Allocator template argument
to do all memory allocations/deallocations.


Yes, which AFAIK is usually using operator new.
 
V

Victor Bazarov

Ioannis said:
Yes, which AFAIK is usually using operator new.

Actually, operator new[]. But what exactly is your point? The
behaviour of the _overloaded_ operator[] for 'std::vector' is
*undefined* if the index passed to it is outside the specified
range. How the memory is allocated is immaterial.

V
 
I

Ioannis Vranos

Victor said:
Yes, which AFAIK is usually using operator new.

Actually, operator new[]. But what exactly is your point? The
behaviour of the _overloaded_ operator[] for 'std::vector' is
*undefined* if the index passed to it is outside the specified
range. How the memory is allocated is immaterial.


Yes we are in agreement here. However this is the same case as arrays
(you said "That's for vector, not for an array").

In both vectors and arrays we can do:

int array[10];

int *p1= array+10;



vector<int> vec(10);

int *p2= &vec[0]+ 10;

or

int *p3= &vec[0]+ vec.size();
 
I

Ioannis Vranos

Ioannis said:
Victor said:
It says this instead of "0<= n <=v.size()".
That's for vector, not for an array. In the case of vector, of
course, it is undefined behavior (and many implementations will
core dump if you attempt to do it).
I suppose a vector is usually implemented by allocating space in the
heap using operator new.
AFAIK, it's always implemented using the Allocator template argument
to do all memory allocations/deallocations.

Yes, which AFAIK is usually using operator new.

Actually, operator new[]. But what exactly is your point? The
behaviour of the _overloaded_ operator[] for 'std::vector' is
*undefined* if the index passed to it is outside the specified
range. How the memory is allocated is immaterial.


Yes we are in agreement here. However this is the same case as arrays
(you said "That's for vector, not for an array").

In both vectors and arrays we can do:

int array[10];

int *p1= array+10;



vector<int> vec(10);

int *p2= &vec[0]+ 10;

or

int *p3= &vec[0]+ vec.size();


and we shouldn't do:

int array[10];

int *p1= &array[10];


vector<int> vec(10);

int *p2= &vec[10];

int *p3= &vec[vec.size()];
 
V

Victor Bazarov

Ioannis said:
Victor said:
It says this instead of "0<= n <=v.size()".
That's for vector, not for an array. In the case of vector, of
course, it is undefined behavior (and many implementations will
core dump if you attempt to do it).
I suppose a vector is usually implemented by allocating space in
the heap using operator new.
AFAIK, it's always implemented using the Allocator template
argument to do all memory allocations/deallocations.

Yes, which AFAIK is usually using operator new.

Actually, operator new[]. But what exactly is your point? The
behaviour of the _overloaded_ operator[] for 'std::vector' is
*undefined* if the index passed to it is outside the specified
range. How the memory is allocated is immaterial.


Yes we are in agreement here. However this is the same case as arrays
(you said "That's for vector, not for an array").

No, I didn't say that.
In both vectors and arrays we can do:

int array[10];

int *p1= array+10;



vector<int> vec(10);

int *p2= &vec[0]+ 10;

or

int *p3= &vec[0]+ vec.size();

Yes, we can. But your post, to which James said "That's for vector
not for an array", included the quote from the Standard where "the
identity &v[n] == &v[0] + n is obeyed for n: 0 <= n < v.size().
For the vector you _cannot_ claim that identity for n == v.size()
*because* the expression

&v[n]

has undefined behaviour for n == v.size(). That's what James has
told you.

V
 
V

Victor Bazarov

Ioannis said:
Ioannis said:
Victor said:
It says this instead of "0<= n <=v.size()".
That's for vector, not for an array. In the case of vector, of
course, it is undefined behavior (and many implementations will
core dump if you attempt to do it).
I suppose a vector is usually implemented by allocating space in
the heap using operator new.
AFAIK, it's always implemented using the Allocator template
argument to do all memory allocations/deallocations.

Yes, which AFAIK is usually using operator new.

Actually, operator new[]. But what exactly is your point? The
behaviour of the _overloaded_ operator[] for 'std::vector' is
*undefined* if the index passed to it is outside the specified
range. How the memory is allocated is immaterial.


Yes we are in agreement here. However this is the same case as arrays
(you said "That's for vector, not for an array").

In both vectors and arrays we can do:

int array[10];

int *p1= array+10;



vector<int> vec(10);

int *p2= &vec[0]+ 10;

or

int *p3= &vec[0]+ vec.size();


and we shouldn't do:

int array[10];

int *p1= &array[10];


vector<int> vec(10);

int *p2= &vec[10];

int *p3= &vec[vec.size()];

Correct, we shouldn't. The array code has undefined behaviour
because it dereferences the one-past-the-end pointer, and the
vector code has undefined behaviour because the Standard defines
the overloaded op[] that way.

V
 
J

James Kanze

Actually, operator new[].

Actually, the ::eek:perator new( size_t ) function. The allocator
can use neither new nor new[] expressions, since it must
allocate without calling the constructor. The standard says
that the default allocator must use ::eek:perator new( size_t ),
and not ::eek:perator new[]( size_t ).

(I suspect that this is for historical reasons. Historically,
there was no ::eek:perator new[]( size_t ) function---both new and
new[] used ::eek:perator new( size_t ). And of course, the system
implementation of ::eek:perator new[]( size_t ) is required to call
::eek:perator new( size_t ) to get the memory.)
But what exactly is your point? The behaviour of the
_overloaded_ operator[] for 'std::vector' is *undefined* if
the index passed to it is outside the specified range. How
the memory is allocated is immaterial.

I was wondering about that as well.
 
J

James Kanze

Victor Bazarov wrote:
Yes we are in agreement here. However this is the same case as arrays
(you said "That's for vector, not for an array").

No it's not.
In both vectors and arrays we can do:
int array[10];
int *p1= array+10;
vector<int> vec(10);
int *p2= &vec[0]+ 10;

int *p3= &vec[0]+ vec.size();

But in one case, you're dealing with a built in type, and
&array[10] is the same as &*(array+10). In the other, we're
dealing with a member function, and not "operators". Don't be
mislead by the similar syntax.

(On my system, &array[10] would work, in the above, but &vec[10]
core dumps. I suspect that this is a frequent case.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top