C - allocating an array of pointers, and usage - typecast safety
I'm working with some legacy code that makes extensive use of this kind of thing:
// Allocate a look-up-table of pointers.
long *pointerLUT = (long *) malloc(sizeof(long) * numPointers);
...
// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
pointerLUT[i] = (long) NewFoo();
}
...
// Ac开发者_运维问答cess the LUT.
Foo *foo = (Foo *) pointerLUT[anIndex];
As you can see, this allocates an array of longs, with the idea of using them generic pointer storage.
Q1. Is this approach safe?
Q2. Style-wise, how could it be improved? Does it need to be? (The typecasting rattles the fear-monkey in me.)
Thanks.
EDIT: I missed where he said "generic pointer storage" in the question. This answer is not correct for that case.
If you are working with pointers to Foo then that's what your code should say.
// Allocate a look-up-table of pointers.
Foo **pointerLUT = (Foo **) malloc(sizeof(Foo *) * numPointers);
// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
pointerLUT[i] = NewFoo(); // NewFoo() should return (Foo *)
}
// Access the LUT.
Foo *foo = pointerLUT[anIndex];
A1: You should replace long
with void *
because sizeof(long)
is not necessarily the same as sizeof(void *)
. For example this would not work on 64-bit Windows setup, where long
is 32 bit and pointers are 64.
A2: If you use C and void *
you would not need to use typecasts, since it's ok to cast from and to void *
.
In C, conversions from pointer to integer types (and vice versa) are implementation defined1 (this means that the implementation must document whether this is supported and/or how it is done). Also there is no guarantee that a pointer to void has the same size or can represent all the same values as a long integer.
It would be better to allocate an array of pointer to void type, for example:
void **pointerLUT = malloc(sizeof (void *) * numPointers);
// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
pointerLUT[i] = NewFoo(); // implicit conversion to void *
}
// Access the LUT.
Foo *foo = pointerLUT[anIndex]; // implicit conversion from void *
1: See section §6.3.2.3 paragraphs 5 and 6:
(5) An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
(6) Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
That code is unportable because there is no guarantee that sizeof(long) == sizeof(void*)
. The code would be better in style if it at least used void*
instead of long
, but that won't be an easy fix to make everywhere.
精彩评论