-4

I am trying to work out the precise equivalent of this code using new instead of malloc. Pointer to pointer to float array[4]:

float (**f)[4] = (float(**)[4])malloc(2 * sizeof(float(*)[4]));
for (int p = 0; p < 2; p++) {
    f[p] = (float(*)[4])malloc(3 * sizeof(float[4]));
}

I have tried a bunch of combinations, but cannot find the correct syntax.

float (**f)[4] = new ...
sid
  • 157
  • 8

3 Answers3

1

You can create a helper class that can new the right thing for you auto-magically via the conversion.

template <unsigned N = 0> struct NewAny;

template <unsigned N> struct NewAny {
    template <typename T>
    operator T * () const { return new T[N]; }
};

template <> struct NewAny<0> {
    template <typename T>
    operator T * () const { return new T; }
};

int main () {
    float (**x)[4] = NewAny<2>();
    delete[] x;
    x = NewAny<>();
    delete x;
}

In your example:

float (**f)[4] = NewAny<2>();
for (int p = 0; p < 2; p++) {
    f[p] = NewAny<3>();
}

Modern C++ teaches to avoid the error prone nature of manual management of dynamically allocated memory by using containers and smart pointers. You could do something like the below to create a D-dimensional vector:

template <typename T, unsigned D> struct Vectorate;

template <unsigned N, typename T, unsigned D>
struct Vectorate<T[N], D> {
    typedef
    typename Vectorate<std::array<T, N>, D>::type type;
};

template <typename T, unsigned D>
struct Vectorate {
    typedef
    typename Vectorate<std::vector<T>, D-1>::type type;
};

template <typename T>
struct Vectorate<T, 0> {
    typedef T type;
};

In your example:

Vectorate<float[4], 2>::type f;
f.resize(2);
for(auto &ff : f) ff.resize(3);
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Except for syntaxic sugar, the first code should be avoided since it would allow any invalid declaration to be accepted (simply write whatever you want in front of NewAny() and the compiler will happily create that for you). For example, in your case, the compiler will allocate an array of 2x `array of 4 float **`, which is not exactly what you want, if I understand the initial question correctly. – xryl669 Mar 21 '19 at 10:20
  • The last example is much more clean but it's not dynamic for the multi dimension array size (which is probably good anyway) – xryl669 Mar 21 '19 at 10:24
  • @xryl669 `NewAny` is just as safe as `new`. If LHS is `T *`, RHS is `new T`. – jxh Mar 21 '19 at 14:46
  • Just remove one star like `float (*f)[4] = NewAny<2>()` and the code will still compile. Unlike `float * t = new float*[2]` where the compiler will choke. That's a subtle bug to spot. – xryl669 Mar 21 '19 at 16:09
  • I agree. It's not criticism, it's perfectly fine. My remark was just to make it clear for an outside observer that using a template based conversion operator removes the *double check* of similar type on both side of the assignment that's usually done with plain old new. It's a neat powerful trick but the downside should be said too. – xryl669 Mar 21 '19 at 16:23
1

Plain old declaration (don't do this):

try
{
    float *** f = new float ** [2];
    for (size_t i = 0; i < 2; i++)
    {
       f[i] = new float * [3];
       for (size_t j = 0; j < 3; j++)
           f[i][j] = new float[4];
    }
} catch(const std::exception & e) { /* This is a pain to deal with to avoid any leak */ }

// Now you can access via f[0][1][2]
// delete[] in reverse order.

A bit better (avoid using many allocations):

try 
{
    typedef float BigArray[3][4];
    BigArray * f = new BigArray[2];
} catch(const std::exception & e) { /* here it's simple */ }
// delete with delete[] f

A bit cumbersome, but you don't care about memory leak anymore:

std::vector<std::vector<std::vector<float>>> f(2, std::vector<std::vector<float>>(3, std::vector<float>(4)));
// Use with f[0][1][2]

Also, as most would say, instead of pointer to pointer to pointer of float, you should store your array in pointer to float instead, it'll be a lot more efficient since you don't need to dereference in 3 steps to access an element, that is, like:

int vectorElems = 4, columns = 3, rows = 2;
int rowStride = vectorElems * columns;  
float * f = new float[rows*columns*vectorElems];
// Access with stride:
f[0 * rowStride + 1 * vectorElems + 2] = 3.14f;

// Delete with delete[] f

There are also template matrix classes (for exemple in opencv) that's doing this properly by providing an overloaded () operator so you can access the object like f(0, 1, 2)

xryl669
  • 3,376
  • 24
  • 47
  • Many thanks. But is it possible to do precisely `float (**f)[4] = new ...`? The syntax interests me. – sid Mar 20 '19 at 22:01
  • No, I'm afraid it's not possible to do so like you've written. The part on the left declare an array of size 4 of pointer to pointer of float **on the stack**. Just like when you write `char buf[3]`, you shouldn't assign a heap allocated pointer to this (`buf = new char[3]` is undefined behavior) – xryl669 Mar 21 '19 at 10:27
  • The closer you could get is `float (**f)[4] = { new float *[3], new float *[3], new float *[3], new float *[3]}`. Yet you'll still need to iterate the array to allocate the inner pointers in the array. – xryl669 Mar 21 '19 at 10:28
  • It's obvious but if the dimension are fixed, you can do `float f[2][3][4]` – xryl669 Mar 21 '19 at 10:29
  • Ah, I see that I misunderstood what float (**f)[4] actually does. I thought it was pointer to pointer to array of 4 floats, but it's actually array of 4 pointers to pointer to float. Am I correct in saying this? – sid Mar 21 '19 at 13:22
  • @sid Your understanding was correct. `f` is a pointer to pointer of array of 4 `float` – jxh Mar 21 '19 at 14:42
  • `float **f[4]` is a (stack allocated) array of 4 pointer to pointer of float. `float (**f)[4]` is a pointer to pointer to 4 floats array. This is very confusing for any reader, so please follow the advice above to use a typedef. – xryl669 Mar 21 '19 at 16:05
  • Sorry, I made a mistake in my previous comment. The correct code is `float **f[4] = { new float *[3], new float *[3], new float *[3], new float *[3]}`. – xryl669 Mar 21 '19 at 16:11
  • If you need to the pointer to pointer to array version, this is how it should be done: `typedef float VFloat[4]; float (**f)[4] = new VFloat *[3];` – xryl669 Mar 21 '19 at 16:18
-4

I'm not sure... But try this:

float **f[4];
    for(int i = 0; i < 4; i++){
        f[i] = (float**)malloc(sizeof(float**));
    }
    printf("%ld", sizeof(f));