39

How do I cast or convert an int* into an int[x]?

First, I know that pointers can be indexed. So I know that I can loop through the pointer and array and manually convert the pointer. (eg. a for loop with arr[i] = p[i]). I want to know if the same result can be achieved in fewer lines of code.

As an example I tried to cast pointer int* c = new int[x] to an array int b[2]

int a    = 1;
int b[2] = { 2, 3 };
int* c   = new int[b[1]];

c[0] = b[0];
c[1] = b[1];
c[2] = a;

I wanted to see what values were where, so I made a simple program to output addresses and values. The output is just below:

Address of {type: int}    &a    =       0031FEF4; a    = 1
Address of {type: int[2]} &b    =       0031FEE4; b    = 0031FEE4
Address of {type: int[2]} &b[0] =       0031FEE4; b[0] = 2
Address of {type: int[2]} &b[1] =       0031FEE8; b[1] = 3
Address of {type: int*}   &c    =       0031FED8; c    = 008428C8
Address of {type: int*}   &c[0] =       008428C8; c[0] = 2
Address of {type: int*}   &c[2] =       008428D0; c[2] = 1

Once I made sure I knew what was where I tried a few things. The first idea that came to mind was to get the address of the second element to the pointer's allocation, then replace the array's memory address with it (see the code just below). Everything I did try ultimately failed, usually with syntax errors.

This is what I tried. I really want this to work, since it would be the simplest solution.

b = &c[1];

This did not work obviously.

Edit: Solution: Don't do it! If it's necessary create a pointer to an array and then point to the array; this is pointless for any purposes I can fathom. For more detailed information see the answer by rodrigo below.

Josh C
  • 1,035
  • 2
  • 14
  • 27
  • 7
    First, why would you need that ? Hint: `int*` and `int [2]` have different purposes (though the second one can decay into the first). – JBL Nov 18 '13 at 11:17
  • I am trying to simplify a file reading function. This is the concept I am grappling with in order to do said simplification. – Josh C Nov 18 '13 at 11:34
  • 2
    Things never get simpler with pointers-to-arrays. Just use plain pointers and sizes. – rodrigo Nov 18 '13 at 11:36
  • @rodrigo Things never get simpler with arrays. Just use `std::vector` – Caleth Jan 11 '19 at 13:36
  • @Caleth Indeed! That was exactly my conclusion in my answer below ;-). – rodrigo Jan 11 '19 at 14:41
  • `memcpy` could be used, yet nasty :( – LinconFive May 22 '20 at 02:25
  • 1
    I often wish I could do this in the debugger, not so much in production code where it's generally not a good practice. For example, if I have an array of float that happens to be represented in the code as a float pointer, it would be nice to expand the array. – Ken Seehart Mar 10 '21 at 03:27
  • 1
    @JBL "First, why would you need that ? Hint: int* and int [2] have different purposes". Exactly! So what if array decaying is not your purpose? It's forced on you inside a function and many times you'd want to treat it as an array like f.i. `std::begin(arr)` – mireazma Mar 29 '21 at 22:31

2 Answers2

47

First of all b is an array, not a pointer, so it is not assignable.

Also, you cannot cast anything to an array type. You can, however, cast to pointer-to-array. Note that in C and C++ pointer-to-arrays are rather uncommon. It is almost always better to use plain pointers, or pointer-to-pointers and avoid pointer-to-arrays.

Anyway, what you ask can be done, more or less:

int (*c)[2] = (int(*)[2])new int[2];

But a typedef will make it easier:

typedef int ai[2];
ai *c = (ai*)new int[2];

And to be safe, the delete should be done using the original type:

delete [](int*)c;

Which is nice if you do it just for fun. For real life, it is usually better to use std::vector.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • So this is the only way to take a memory address and make it the start of an array? By changing the way I define said array; turning it into a pointer to an array and then casting to that? – Josh C Nov 18 '13 at 11:53
  • 6
    Actually the C way is to use the pointer as-is: remember that you can subscript a pointer just fine. The cast to pointer-to-array is mostly useless, except maybe in multi-dimension arrays. I mean, you can think of a `int*` as pointing to a single integer, or as pointing to the first of an array of integers. – rodrigo Nov 18 '13 at 11:54
  • 3
    This undefined behavior in C++. `int*` and `int(*)[2]` are two different pointer types. You are essentially doing an illegal `reinterpret_case` from one type to another and violating the _strict aliasing rule_. – ThomasMcLeod Oct 28 '18 at 03:22
  • 1
    @ThomasMcLeod No time to look it up in the standard, but I believe that an aggregate type and the type of its first element are *not unrelated* and that pointers to each can be cast between. – Peter - Reinstate Monica Aug 20 '19 at 12:23
  • @PeterA.Schneider § 6.9.2(4) of the C++17 standard: "An array object and its first element are not pointer-interconvertible, even though they have the same address." – ThomasMcLeod Aug 21 '19 at 19:59
  • @PeterA.Schneider: That note in the standard says that those pointers are not _pointer-interconvertible_, but does not say what _pointer-interconvertible_ means. From other pieces of the standard, I'm guessing that _pointer-interconvertible_ just means that the pointer address may be changed by `static_cast<>`. See in particular the last para of `static_cast`: "[if the type is not pointer-interconvertible then] then the pointer value is unchanged by the conversion"; that suggests that if the types are pointer-interconvertible, then the pointer type may be changed by the conversion. – rodrigo Aug 21 '19 at 23:17
  • @ThomasMcLeod How can I violate the aliasing rule if I access an int through an int pointer? *Of course* I can do that, we do it all the time! We have **two distinct questions** here: **1.** Does the pointer have the correct numerical value? (Note that I could validly e.g. cast a literal integer value for hardware access!) The answer is an undisputed yes. **2.** Does the resulting expression have the object's type (here: int)? The answer is yes. The aliasing rule is for something else: It forbids accessing this object through pointers of dissimilar types, like floats. – Peter - Reinstate Monica Aug 22 '19 at 05:07
  • @ThomasMcLeod: he @ from my previous comment was for Thomas, not for Peter, sorry. – rodrigo Aug 22 '19 at 07:51
  • @PeterA.Schneider, in C++ and `int` and `array of int` are not similar types for the purposes of pointer aliasing. You may be thinking of how a array of int can contextually "decay" into a pointer to int by way of implicit conversion. However, this is different than accessing the first element (or any element) of an array through a **pointer to array of int**. The compiler is entitled to make assumptions about pointers to types and attempting to access an element of an array through a pointer to entire array violates one of these assumptions. – ThomasMcLeod Aug 22 '19 at 16:03
  • 1
    @ThomasMcLeod Actually casting the `int *` to an `int(*)[]` is indeed an aliasing violation (and your original remark is correct) because there never was an array object -- funny as that seems since we usually allocate array "substitutes" --, but the reverse cast would be OK, provided the address is indeed the location of an array. I had that latter cast in mind. – Peter - Reinstate Monica Aug 22 '19 at 17:44
  • Is it possible to use this trick somehow to pass T* to function that takes T[] ? E.g. in third party library I have the following: MMatrix( const float m[4][4] ); and I want to pass float* to this constructor (technically I have std::array ). Is it possible? – Dmitrii Motorygin Aug 04 '20 at 09:26
  • @DmitriiMotorygin: I don't think so: if a function takes a `std::array` you cannot just pass a pointer, you need to pass _by copy_ the whole 16 values. – rodrigo Aug 04 '20 at 10:09
2

Though you can't reassign an array identifier.. sometimes the spirit of what you're doing allows you to simply create a reference and masquerade yourself as an array. Note: this is just a slight extension of rodrigo's answer... and it is still worth mentioning that there is probably a better way to accomplish whatever the task is.

#include <iostream>

int main() {
    int x[1000] = {0};
    for(int i = 0; i < 10; ++i) {
        int (&sub_x)[100] = *(int(*)[100])(&x[i*100]);
        //going right to left basically:
        // 1. x[i*100] -- we take an element of x
        // 2. &x[N] -- we take the address of the element
        // 3. (int(*)[100]) -- we cast it to a pointer to int[100]
        // 4. *(...) -- lastly we dereference the pointer to get an lvalue
        // 5. int (&sub_x)[100] -- we create the reference `sub_x` of type int[100]
        for(int j = 0; j < 100; ++j) {
            sub_x[j] = (i*100)+j;
        }
    }
    for(int i = 0; i < 1000; ++i) {
        if(i != 0) {
            std::cout << ", ";
        }
        std::cout << x[i];
    }
    std::cout << std::endl;
}

As you'd expect the output just ends up printing 0-999 with no gaps

output:

0, 1, 2, ..., 999
Josh C
  • 1,035
  • 2
  • 14
  • 27