Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to convert a pointer/reference to a fixed array size to a smaller size

Tags:

Is it legal as per the C++ standard to convert a pointer or reference to a fixed array (e.g. T(*)[N] or T(&)[N]) to a pointer or reference to a smaller fixed array of the same type and CV qualification (e.g. T(*)[M] or T(&)[M])?

Basically, would this always be well-formed for all instantiations of T (regardless of layout-type):

void consume(T(&array)[2]);  void receive(T(&array)[6]) {   consume(reinterpret_cast<T(&)[2]>(array));    } 

I don't see any references to this being a valid conversion in:

  • expr.reinterpret.cast,
  • expr.static.cast,
  • conv.array, or even
  • basic.types

However, it appears that all major compilers accept this and generate proper code even when optimized when using T = std::string (compiler explorer)(not that this proves much, if it is undefined behavior).

It's my understanding that this should be illegal as per the type-system, since an object of T[2] was never truly created, which means a reference of T(&)[2] would be invalid.


I'm tagging this question c++11 because this is the version I am most interested in the answer for, but I would be curious to know whether this answer is different in newer versions a well.

like image 618
Human-Compiler Avatar asked Oct 26 '20 15:10

Human-Compiler


People also ask

What is fixed sized array?

A fixed array is an array for which the size or length is determined when the array is created and/or allocated. A dynamic array is a random access, variable-size list data structure that allows elements to be added or removed. It is supplied with standard libraries in many modern programming languages.

Are arrays in C fixed size?

Arrays a kind of data structure that can store a fixed-size sequential collection of elements of the same type.

Why declare a pointer to an array of different sizes?

The neat thing about declaring a pointer like this is that you will get a compile time error if you try to assign a pointer of an array of different size to p. It will also give you a compile time error if you try to assign the value of a simple char pointer to p.

Is it legal to use array names as pointers in C?

Assuming you have some understanding of pointers in C, let us start: An array name is a constant pointer to the first element of the array. It is legal to use array names as constant pointers, and vice versa. Therefore, * (balance + 4) is a legitimate way of accessing the data at balance [4].

Can I assign a pointer of a different size to P?

The neat thing about declaring a pointer like this is that you will get a compile time error if you try to assign a pointer of an array of different size to p. It will also give you a compile time error if you try to assign the value of a simple char pointer to p. I tried this with gcc and it seems to work with ANSI, C89 and C99.

What is the pointer to the balance of an array?

Assuming you have some understanding of pointers in C, let us start: An array name is a constant pointer to the first element of the array. Therefore, in the declaration − double balance ; balance is a pointer to &balance, which is the address of the first element of the array balance.


1 Answers

There’s not much to say here except no, in any language version: the types are simply unrelated. C++20 does allow conversion from T (*)[N] to T (*)[] (and similarly for references), but that doesn’t mean you can treat two different Ns equivalently. The closest you’re going to get to a “reference” for this rule is [conv.array]/1 (“The result is a pointer to the first element of the array.”, which T[2] does not exist in your example) and a note in [defns.undefined] (“Undefined behavior may be expected when this document omits any explicit definition of behavior”).

Part of the reason that compilers don’t “catch” you is that such reinterpret_casts are valid to return to the real type of an object after another reinterpret_cast used to “sneak” it through an interface that expects a pointer or reference to a different type (but doesn’t use it as that type!). That means that the code as given is legitimate, but the obvious sort of definition for consume and caller for receive would together cause undefined behavior. (The other part is that optimizers often leave code alone that’s always undefined unless it can eliminate a branch.)

like image 66
Davis Herring Avatar answered Oct 12 '22 03:10

Davis Herring