Consider this:
int main(int, char **) {
int variable = 21;
int array[1] = {21};
using ArrayOf1Int = int[1];
(*reinterpret_cast<ArrayOf1Int *>(&variable))[0] = 42;
*reinterpret_cast<int *>(&array) = 42;
return 0;
}
Did I just violate the strict aliasing rule?
Or, as in this comment that led me to this question: Is a variable an array of size 1?
Note that I tagged this as language-lawyer question. Thus I'm not interested in -fno-strict-aliasing
or compiler specific behavior, but instead in what's said in the standard. Also I think it would be interesting to know if and how this changed between C++03, C++11, C++14, and newer versions.
Declaring Arrays:The array is indexed from 0 to size-1. The size (in brackets) must be an integer literal or a constant variable. The compiler uses the size to determine how much space to allocate (i.e. how many bytes).
Variable length arrays are also known as runtime sized or variable sized arrays. The size of such arrays is defined at run-time. Variably modified types include variable length arrays and pointers to variable length arrays.
An array is a variable containing multiple values. Any variable may be used as an array. There is no maximum limit to the size of an array, nor any requirement that member variables be indexed or assigned contiguously. Arrays are zero-based: the first element is indexed with the number 0.
An array is a collection of similar data elements stored at contiguous memory locations. It is the simplest data structure where each data element can be accessed directly by only using its index number.
Clearly, if an object were a variable of an array of size one, you could initialise a reference to an array of size one with an object:
int variable{21};
int (&array)[1] = variable; // illegal
However, the initialisation is illegal. The relevant clause for this is Clause 4 [conv] (Standard Conversions) which stated in paragraph 1:
Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions.
This clause is too long to quote here but it has nothing to say about a conversion of an object to a reference of an array of any size. Similarly, the section in reinterpret_cast
(5.2.10 [expr.reinterpret.cast]) does not spell out any behaviour involving arrays but does spell out this exclusion in paragraph 1:
... Conversions that can be performed explicitly using
reinterpret_cast
are listed below. No other conversion can be performed explicitly usingreinterpret_cast
.
I don't think there is an explicit statement that an object is not an array of one object but there are sufficient omissions to make the case implicitly. The guarantee given by the standard relating objects and array is that pointer to an object behave as if they are pointing to array of size 1 (5.7 [expr.add] paragraph 4):
For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
The presence of this statement also implies that array objects and nonarray objects are different entities: if they were considered the same this statement wouldn't be necessary to start with.
With respect to prior (or future) versions of the standard: although the exact words in the different clauses may have changed, the overall situation didn't change: objects and arrays were always different entities and, so far, I'm not aware of an intent to change that.
reinterpret_cast
only behaves predictably in C++11 and above, so neither line is guaranteed to have defined behaviour before C++11. We'll proceed assuming C++11 or above.
First line
(*reinterpret_cast<decltype(&array)>(&variable))[0] = 42;
In this line, dereferencing the reinterpret_cast
yields a glvalue but does not access the int
object through that glvalue. By the time the int
object is accessed, the glvalue referring to the array has already been decayed into a pointer to that object (that is, an int*
).
However, one can "contrive" a case that looks like it might contain a strict aliasing violation, like so:
struct S {
int a[1];
};
int variable = 42;
S s = reinterpret_cast<S&>(variable);
This does not violate strict aliasing because you are allowed to access an object through a subobject of an aggregate or union type. (This rule has existed since C++98.)
Second line
*reinterpret_cast<decltype(&variable)>(&array) = 42;
The reinterpret_cast
is guaranteed to give a pointer to the first subobject of the array, which is an int
object, so assigning to it through an int
pointer is well-defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With