Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer interconvertibility vs having the same address

The working draft of the standard N4659 says:

[basic.compound]
If two objects are pointer-interconvertible, then they have the same address

and then notes that

An array object and its first element are not pointer-interconvertible, even though they have the same address

What is the rationale for making an array object and its first element non-pointer-interconvertible? More generally, what is the rationale for distinguishing the notion of pointer-interconvertibility from the notion of having the same address? Isn't there a contradiction in there somewhere?

It would appear that given this sequence of statements

int a[10];  void* p1 = static_cast<void*>(&a[0]); void* p2 = static_cast<void*>(&a);  int* i1 = static_cast<int*>(p1); int* i2 = static_cast<int*>(p2); 

we have p1 == p2, however, i1 is well defined and using i2 would result in UB.

like image 856
n. 1.8e9-where's-my-share m. Avatar asked Dec 21 '17 11:12

n. 1.8e9-where's-my-share m.


People also ask

How do you assign the address of an array to a pointer?

To assign a pointer to the array we can use the following declaration... int a[10]; int *pa = &a[0]; Basically this assigns the memory address of a[0] the first element in array a to pointer of type int .

Is pointer the same as array?

An array is considered to be the same thing as a pointer to the first item in the array. That rule has several consequences. An array of integers has type int*. C++ separates the issue of allocating an array from the issue of using an array.


2 Answers

There are apparently existing implementations that optimize based on this. Consider:

struct A {     double x[4];     int n; };  void g(double* p);  int f() {     A a { {}, 42 };     g(&a.x[1]);     return a.n; // optimized to return 42;                 // valid only if you can't validly obtain &a.n from &a.x[1] } 

Given p = &a.x[1];, g might attempt to obtain access to a.n by reinterpret_cast<A*>(reinterpret_cast<double(*)[4]>(p - 1))->n. If the inner cast successfully yielded a pointer to a.x, then the outer cast will yield a pointer to a, giving the class member access defined behavior and thus outlawing the optimization.

like image 134
T.C. Avatar answered Sep 22 '22 09:09

T.C.


More generally, what is the rationale for distinguishing the notion of pointer-interconvertibility from the notion of having the same address?

It is hard if not impossible to answer why certain decisions are made by the standard, but this is my take.

Logically, pointers points to objects, not addresses. Addresses are the value representations of pointers. The distinction is particularly important when reusing the space of an object containing const members

struct S {     const int i; };  S s = {42}; auto ps = &s; new (ps) S{420}; foo(ps->i);  // UB, requires std::launder 

That a pointer with the same value representation can be used as if it were the same pointer should be thought of as the special case instead of the other way round.

Practically, the standard tries to place as little restriction as possible on implementations. Pointer-interconvertibility is the condition that pointers may be reinterpret_cast and yield the correct result. Seeing as how reinterpret_cast is meant to be compiled into nothing, it also means the pointers share the same value representation. Since that places more restrictions on implementations, the condition won't be given without compelling reasons.

like image 39
Passer By Avatar answered Sep 26 '22 09:09

Passer By