问题
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;
}
回答1:
The generic pointer type in C is void*
. That does not mean that void**
is also a generic pointer type, the rule does not apply recursively.
Rather, a conversion from void**
to float**
or the other way around is an invalid pointer conversion. They are not compatible types - the compiler must issue a diagnostic message. Ignoring the warning might result in misaligned data or strict aliasing issues - in either case bugs.
What should I do?
Fix the code so that it doesn't contain forbidden pointer conversions.
Are newer/other compilers aware of this kind of cast?
This particular rule has not changed since the first standardization of C.
As for how you should fix your code... you shouldn't. You should rewrite it from scratch so that it allocates arrays and not "pointer-based look-up tables". There is no obvious reason why you would benefit from such a look-up-table here, but many reasons why you should avoid it.
In addition, your code would be much more readable if you use pointers to VLA.
See Correctly allocating multi-dimensional arrays for examples of how to do this proper (code example at the very bottom of the answer).
回答2:
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 tofloat **
. 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 castingvoid **
tofloat **
, you're treating an array ofvoid *
as if it was and array offloat *
. 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);
来源:https://stackoverflow.com/questions/46070582/should-i-cast-void-return-in-visual-studio