Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a variable an array of size 1?

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.

like image 890
Daniel Jour Avatar asked Sep 08 '16 22:09

Daniel Jour


People also ask

Can you have an array of size 1?

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).

Can an array size be a variable?

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.

Is a variable an array?

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.

What is an array 1?

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.


2 Answers

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 using reinterpret_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.

like image 63
Dietmar Kühl Avatar answered Oct 19 '22 16:10

Dietmar Kühl


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.

like image 31
Brian Bi Avatar answered Oct 19 '22 15:10

Brian Bi