The declaration in the first line is also a definition. (§3.1(2))
It creates the array object. (§1.8(1))
An object can be accessed via multiple lvalues
due to the aliasing rules. (§3.10(10)) In particular, the objects on the
right hand side may be legally accessed (aliased) through char pointers.
Lets look at a sentence in the array definition and then disambiguate 'contiguous'.
"An object of array type contains a contiguously allocated non-empty set
of N subobjects of type T." [dcl.array] §8.3.4.
Disambiguation
We start from the binary symmetric relation 'contiguous' for char objects, which should be obvious. ('iff' is short for 'if and only if', sets and sequences are mathematical ones, not C++ containers) If you can
link to a better or more acknowledged definition, comment.
A sequence x_1 ... x_N of char objects is contiguous iff
x_i and x_{i+1} are contiguous in memory for all i=1...N-1.
A set M of char objects is contiguous iff the objects in
M can be numbered, x_1 ...x_N, say, such that the sequence (x_i)_i is contiguous.
That is, iff M is the image of a contiguous, injective sequence.
Two sets M_1, M_2 of char objects are contiguous iff there
exist x_1 in M_1 and x_2 in M_2 such that x_1 and x_2 are contiguous.
A sequence M_1 ... M_N of sets of char objects is contiguous iff
M_i and M_{i+1} are contiguous for all i=1...N-1.
A set of sets of char objects is contiguous iff it is the image of
a contiguous, injective sequence of sets of char objects.
Now which version of 'contiguous' to apply? Linguistic overload resolution:
1) 'contiguous' may refer to 'allocation'. As an allocation function call provides a
subset of the available char objects, this would invoke the set-of-chars variant. That is,
the set of all char objects that occur in any of the N subobjects would be meant to be contiguous.
2) 'contiguous' may refer to 'set'. This would invoke the set-of-sets-of-chars variant with every subobject considered as a set of char objects.
What does this mean? First, while the authors numbered the array subobjects a[0] ... a[N-1], they chose not to say anything about the
order of subobjects in memory: they used 'set' instead of 'sequence'.
They described the allocation as contiguous, but they do not say that
a[j] and a[j+1] are contiguous in memory. Also, they chose not to write down the
straightforward formula involving (char*) pointers and sizeof(). While it looks like they
deliberately separated contiguity from ordering concerns,
§5.9 (3) requires one and the same ordering for array subobjects of all types.
If pointers point to two different elements of the same array, or a subobject thereof, the pointer
to the element with the higher subscript compares greater.
Now do the bytes that make up the array subobjects qualify as
subobjects in the sense of the above quote? Reading §1.8(2) and Complete object or subobject?
the answer is: No, at least not for arrays whose elements don't contain subobjects and are no arrays of chars, e.g. arrays of ints. So we may find examples where no particular ordering is imposed on the array elements.
But for the moment let's assume that our array subobjects are populated with chars only.
What does this mean considering the two possible interpretations of 'contiguous'?
1) We have a contiguous set of bytes that coincides with an ordered set of subobjects.
Then the claim in the OP is unconditionally true.
2) We have a contiguous sequence of subobjects, each of which may be non-contiguous individually.
This may happen in two ways: either the subobjects may have gaps, that is, they
contain two char objects at distance greater than sizeof(subobject)-1. Or the
subobjects may be distributed among different sequences of contiguous bytes.
In case 2) there is no guarantee that that the claim in the OP is true.
Therefore, it is important to be clear about what 'contiguous' means.
Finally, here's an example of an implementation where no obvious ordering is imposed on the array subobjects by §5.9 because the array subobjects don't have subobjects themselves. Readers raised concerns that this would contradict the standard in other places, but no definite contradiction has been demonstrated yet.
Assume T is int, and we have one particular conforming implementation that behaves as expected naively with one exception:
It allocates arrays of ints in reversed memory order,
putting the array's first element at the high-memory-address end of the object:
a[N-1], a[N-2], ... a[0]
instead of
a[0], a[1], ... a[N-1]
This implementation satisfies any reasonable contiguity
requirement, so we don't have to agree on a single interpretation of
'contiguous' to proceed with the argument.
Then if p points to a, mapping p to &a[0] (invoking [conv.array]) would make the pointer jump near the high memory end of a.
As array arithmetic has to be compatible with pointer arithmetic, we'd also have
int * p= &intVariable;
(char*)(p+1) + sizeof(int) == (char*)p
and
int a[N];
(char*)(void*)&a[n] + n*sizeof(int)==(char*)(void*)&a[0], (0<=n<N)
Then, for T=int, there is no guarantee that the claim in the original post is true.
edit history: removed and reintroduced in modified form a possibly erroneous shortcut that was due to not applying a relevant part of the pointer < relation specification. It has not been determined yet whether this was justified or not, but the main argument about contiguity comes through anyway.
0<=n<=N
, and that also applies to scalars. – Scarfacep + 1
is defined, but that doesn't meanp + anything
is also defined. The published C++14 standard text is recognized to have some defects in this area. Some have been fixed (see P0137), some still need work (for example, CWG1701). Keep in mind that P0137 clarified thata
anda[0]
are not pointer-interconvertible. It's not just about layout, it's also about restrictions imposed to allow optimizations. – Turquoisechar*
to the end of memory? No, that will never be allowed by the standard. That would mean that if you had a function taking achar*
, you could legally access the value of any object in the program through that pointer, making the aliasing rules (for example) irrelevant. – Turquoisea[b]
is equivalent to*(a+b)
, which is also the reason why it is also equivalent tob[a]
. With C++ operator overloading, this principle has deteriorated somewhat, nevetheless it places a very firm restriction on the layout of any C-style array. And that restriction guarantees that your equivalence must be true. – Condillaclaunder
it after each addition, it should work...? – Airinglaunder
a pointer past the end of an object, because the reachability requirement in [ptr.launder] can't be satisfied. The pointer doesn't point to an actual object, so there's no associated storage, so no bytes can be reachable from that pointer. Besides, the notion of reachable seems to be defined only if we start from a pointer value that points to an object. – Turquoisememcpy
a wholeint[7]
using anint*
to its first member (in a way, similarly to how we can't flatten anint[3][7]
to anint[21]
); we have to either use a pointer to the array itself, orreinterpret_cast
theint*
toint (*)[7]
andlaunder
it. And if the "array" was created bynew int[nonconstant]
... we can'tmemcpy
that at all. Hmmm... It would be nice if CWG1701 and 2182 could be resolved in time for C++17. – Turquoises
containsunsigned char foo[16];
as its first member, laundering that pointer should give anunsigned char*
that can access the whole struct. – Incommensurableunsigned char (*)[16]
pointing to that array to aPODS*
wouldn't even need alaunder
, as the two objects are pointer-interconvertible; areinterpret_cast
is enough. Going from anunsigned char*
pointing to the first element of the array member to a pointer to the array, however, requires alaunder
. The array element and the array itself are not pointer-interconvertible, so the array element and the containing PODS aren't either. The subsequentunsigned char*
thing requires a suitable resolution of CWG1701. – Turquoiseunsigned char* p = std::launder(foo);
will give you ap
that allows you to access the wholePODS
object, I don't think that's how it works. I think what's needed isunsigned char* p = reinterpret_cast<unsigned char*>(std::launder(reinterpret_cast<PODS*>(foo)));
. The inner cast gives a pointer that, although of typePODS*
, still points to the firstunsigned char
element offoo
. Thelaunder
gives a pointer that points to the whole struct, and the outer cast yields a pointer that still points to the struct, and so can access the whole object. – Turquoisechar*
that can only access an inner object versus one that can access an outer object, and where code might receive a pointer and a size, and need to be able to usememcpy
to copy the indicated number of bytes without necessarily knowing the enclosing type. – IncommensurablePODS
object are reachable through a pointer to the first array element, so we can'tlaunder
directly. Ifp_elem
points to the first array element, then we need this:unsigned char* p = reinterpret_cast<unsigned char*>(reinterpret_cast<PODS*>(std::launder(reinterpret_cast<unsigned char (*)[16]>(p_elem))));
. – Turquoise