Is It Legal to Cast a Pointer to Array Reference Using Static_Cast in C++

Is it legal to cast a pointer to array reference using static_cast in C++?

Short answer: You are right. The cast is safe only if pValues is of type T[N] and both of the cases you mention (different size, dynamically allocated array) will most likely lead to undefined behavior.


The nice thing about static_cast is that some additional checks are made in compile time so if it seems that you are doing something wrong, compiler will complain about it (compared to ugly C-style cast that allows you to do almost anything), e.g.:

struct A { int i; };
struct C { double d; };

int main() {
A a;
// C* c = (C*) &a; // possible to compile, but leads to undefined behavior
C* c = static_cast<C*>(&a);
}

will give you: invalid static_cast from type ‘A*’ to type ‘C*’

In this case you cast to void*, which from the view of checks that can be made in compile time is legal for almost anything, and vice versa: void* can be cast back to almost anything as well, which makes the usage of static_cast completely useless at first place since these checks become useless.

For the previous example:

C* c = static_cast<C*>(static_cast<void*>(&a));

is no better than:

C* c = (C*) &a;

and will most likely lead to incorrect usage of this pointer and undefined behavior with it.


In other words:

A arr[N];
A (&ref)[N] = *static_cast<A(*)[N]>(&arr);

is safe and just fine. But once you start abusing static_cast<void*> there are no guarantees at all about what will actually happen because even stuff like:

C *pC = new C;
A (&ref2)[N] = *static_cast<A(*)[N]>(static_cast<void*>(&pC));

becomes possible.

Is it possible to reinterpret pointer as dimensioned array reference?

It's ugly:

double arr[4];
double* d = arr;

double (&a)[4] = *static_cast<double(*)[4]>(static_cast<void*>(d));

Be sure the array type matches what the pointer originally came from.

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

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

Why can't we static cast a char pointer to an int pointer?

Firstly, the static_cast<int*>(&c) will fail because static_cast only allows a very specific subset of type conversions. For instance, you can static_cast between numeric types, or between two class types which are related by inheritance (i.e. one inherits the other at some point in the inheritance hierarchy).

What you're doing is simply trying to "reinterpret" the address as an address to a different type. The reason being that C++ std::ostream overloads operator<< for const char*. If you only want to print the address itself, standard convention is to convert to void* via static_cast<void*>(&c) or just (void*)&c if you're ok with C-style casts. void* is basically a pointer with no type information, which makes it perfect if all you want to do is print out the value of the address itself.

The reason the C-style case compiles is because a C-style cast employs very ... shall we say risky ... methods to perform the conversion. It's allowed to perform const_cast, reinterpret_cast, and static_cast - whatever is necessary - in order to perform the cast you specified. This behavior is detailed on cppreference.com.

As for why the example of printing the char* directly only prints a single character despite not being terminated: undefined behavior is undefined. Apparently, it just so happens that (with this compiler) the executable happens to have a zero (null) byte immediately following it, wherever it may be. The compiler might even realize you assign a value but never modify it, so it might not even be in the stack (it might be in the data segment of the executable). The moral of the story is just not to invoke undefined behavior.

Reference to array in memory

I find the notion of using an array reference a bit convoluted, like tadman mentioned. But you can do it as you'd do with any type, by dereferencing a pointer.

int (&my_array)[2] = *reinterpret_cast<int(*)[2]>(0x18FBDA);

Also, if you are going to do such a cast, don't let it appear innocent by doing a c-style cast. Such a thing should stand out IMO.

When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?

static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones). In many cases, explicitly stating static_cast isn't necessary, but it's important to note that the T(something) syntax is equivalent to (T)something and should be avoided (more on that later). A T(something, something_else) is safe, however, and guaranteed to call the constructor.

static_cast can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast through virtual inheritance. It does not do checking, however, and it is undefined behavior to static_cast down a hierarchy to a type that isn't actually the type of the object.


const_cast can be used to remove or add const to a variable; no other C++ cast is capable of removing it (not even reinterpret_cast). It is important to note that modifying a formerly const value is only undefined if the original variable is const; if you use it to take the const off a reference to something that wasn't declared with const, it is safe. This can be useful when overloading member functions based on const, for instance. It can also be used to add const to an object, such as to call a member function overload.

const_cast also works similarly on volatile, though that's less common.


dynamic_cast is exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards – you can cast sideways or even up another chain. The dynamic_cast will seek out the desired object and return it if possible. If it can't, it will return nullptr in the case of a pointer, or throw std::bad_cast in the case of a reference.

dynamic_cast has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't using virtual inheritance. It also can only go through public inheritance - it will always fail to travel through protected or private inheritance. This is rarely an issue, however, as such forms of inheritance are rare.


reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another — such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions that reinterpret_cast cannot do, too. It's used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of a pointer to aligned data.


C-style cast and function-style cast are casts using (type)object or type(object), respectively, and are functionally equivalent. They are defined as the first of the following which succeeds:

  • const_cast
  • static_cast (though ignoring access restrictions)
  • static_cast (see above), then const_cast
  • reinterpret_cast
  • reinterpret_cast, then const_cast

It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a reinterpret_cast, and the latter should be preferred when explicit casting is needed, unless you are sure static_cast will succeed or reinterpret_cast will fail. Even then, consider the longer, more explicit option.

C-style casts also ignore access control when performing a static_cast, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.



Related Topics



Leave a reply



Submit