Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a void ** return [duplicate]

I compile some code with visual studio 9.0 (2008).

The behave as expected, but when I allocate some 2D array with some hand made functions, Visual-Studio generates some C4133 warning:

void ** alloc_2d(int w, int h, size_t type_size);
void free_d2(void ** mem);

int main (void)
{        
    float ** data;

    /* Here is generated a C4133 warning:
       "incompatible type from void ** to float **" */
    data = alloc_2d(100, 100, sizeof **data);

    /* do things with data */

    /* free data */
    free_2d(data);

    return 0;
}

I understand why this warning is generated, but I wonder what I should do to make it quiet.

What should I do?

  • Live with the warning?
  • Disable the warning (dangerous I think)?
  • Disable the warning around the alloc_2d calls (with some macros specific to visual studio)?
  • cast the function return (But [Do I cast the result of malloc?)

Second question:

  • Are newer/other compilers aware of this kind of cast?

Behind the void** are hidden two arrays: one big to store all data I need to be contiguous, and one other to browse through differents lines.

The implementation looks like (I removed the error checking)

void **alloc_2D_array(int w, int h, size_t size)
{
    void ** mem = malloc(w * sizeof *mem);
    *mem = malloc(w*h*size);
    for (i = 1; i < w; ++i)
    {
        mem[i] = (void*)((char*)mem[0] + i*w*size);
    }
    return mem;
}
like image 740
Mathieu Avatar asked Jan 25 '26 20:01

Mathieu


1 Answers

From the signature, I assume your function is implemented roughly like this (plus error checking I'm leaving out here for brevity):

void **alloc_2d(int w, int h, size_t type_size)
{
    void **pointers = malloc(h * sizeof *pointers);
    for (size_t i = 0; i < h; ++i)
    {
        pointers[i] = malloc(w * type_size);
    }
    return pointers;
}

Some remarks on this:

  • The result of this function is not a 2d array, but an array of pointers to arrays. It can be used somewhat similar to a real 2d array, but comes with some overhead. A real 2d array would be one contiguous block of size w * h * type_size.
  • This code is in fact unsafe and could invoke undefined behavior when you cast the void ** returned from it to float **. This is because there's no guarantee that pointers to different types have the same representation -- they could even have different sizes. See also Are there any platforms where pointers to different types have different sizes?. By casting void ** to float **, you're treating an array of void * as if it was and array of float *. Although probably fine on your typical modern PC platform, this can go completely wrong.

That said, you could avoid the cast by simply having your function return void * instead of void **, but this would only hide the problem: void * is the generic pointer type and the compiler will allow implicit conversion to any other pointer type, but you'd still access your void * array using the wrong pointer type that way.


I suggest allocating a flat array instead and calculate the offsets manually, like this:

size_t rows = 100;
size_t cols = 100;
float *data = malloc(cols * rows * sizeof *data);

data[cols*5 + 3] = 1.41;
// ...

free(data);

As an alternative, you can use variable-length arrays (obligatory in C99, optional but almost always supported in C11 -- maybe not supported by Microsoft ....) to dynamically allocate a real 2d array:

size_t rows = 100;
size_t cols = 100;
float (*data)[cols] = malloc(rows * sizeof *data);

data[5][3] = 1.41;
// ...
free(data);

Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!