Re: manipulating void* in array



On Mon, 18 Jul 2005 06:55:52 -0700, Stijn van Dongen wrote:
> Dave Thompson wrote:
>> On 13 Jul 2005 15:51:04 -0700, "Stijn van Dongen" <svd@xxxxxxxxxxxx>
>> wrote:

<snip>

>> > Now I would like to have a function, say hash_keys, which returns all
>> > keys in the hash as a pointer to a malloced array of void*. However,
>> > there is not really a type I can use.

The correct type is void** as Dave pointed out.

struct foo ** would be preferable but I understand that your function
needs to be generic.

>> > I see two possible solutions.
>> > The first is to typedef a struct containing a void pointer:
>> >
>> > typedef struct {
>> > void* pp;
>> > } genpp;
>> >
>> > and have hash_keys return genpp* (the size could be written in an
>> > int* argument
>> > to hash_keys). This would make accessing the keys cumbersome.
>> >
>> > Solution 2) is illegal C (I think), but it's tempting. hash_keys
>> > would construct an array of void*-sized elements and copy its key
>> > pointers there using char* arithmetic.

But if I follow correctly, the key pointers are already void* type, so
you can use a direct assignment rather than byte-by-byte copying.

>> > It would return void* and the
>> > caller would cast it to foo**.

As Dave pointed out, this is not a portable cast.

>> > For one thing, this assumes
>> > sizeof(void*) is sizeof(foo*) for all possible foo.

You're right that that's not a portable assumption. But how does foo*
come into it? I thought your key pointer type was void*.

>> > For another, I
>> > see nothing in the standard on void* that would support any of this.
>> > However, it feels as if approach 2) is conceptually extremely similar
>> > to the first and I suspect it would work on nearly all
>> > platforms/compilers.
>>
>> An array of void*, addressed as void**, and an array of struct
>> containing only void*, addressed as genpp*, are indeed similar. And in
>> fact are very likely (though not guaranteed) to be laid out the same.
>
> I think the array of void* has to be adressed as void*; AFAIK there is
> no type 'void**' in C (as you cannot dereference a void* pointer).

A void ** type is valid. The result of dereferencing a void ** pointer
is a void * pointer, which is allowed.

Your second solution is workable.

To expand on Dave's suggestion, here is how to modify your approach:

a) return void**, not void*
b) copy the pointers directly rather than byte-by-byte. If a
conversion is required (afaict it isn't as the src and dest are both
void*) then rely on these properties of void* pointers (N869, 6.3.2.3):
"A pointer to void may be converted to or from a pointer to any
incomplete or object type. A pointer to any incomplete or object
type may be converted to a pointer to void and back again; the result
shall compare equal to the original pointer."
c) don't cast the return to foo** (as Dave said this is not portable);
instead cast an element at a time:

struct foo *my_foo_ptr = returned_void_ptr_array[i]; /* cast not
req'd*/
struct foo my_foo_object = *my_foo_ptr;

or

struct foo my_foo_object = *(struct foo *)returned_void_ptr_array[i];
/* cast required */

>> If so, the logical thing to do is to create and return an array of
>> void*, and the caller, who presumably knows the type of what was (or
>> should have been) stored, then converts that to a foo* before using it,
>> either by explicitly casting or implicitly by assigning to its own
>> (copy) pointer.
>
> The reason why I don't want to do that is because it implies I'll have a
> whole array of *copies* of my foo things. Given any one foo (presumably
> some struct), I never want to duplicate it, and only pass pointers to it
> around. Having copies around would cause endless trouble with ownership
> issues. In the current setup returning foo* is also impossible since the
> hash routines never (can) dereference their void* key arguments. Which
> is a good thing. It could be achieved by passing another callback to the
> hash table creation routine of course. But I rather have a foo** array
> or a genpp* array as described above.

The void ** approach doesn't require copies, but depending on usage it
may require typecasts.

If you can't accept the typecasting, then yes, a callback will be
necessary.
Something like (untested code):

1) declare your return array in hash_keys as
void *retarray = NULL; /* NOT void ** since this implies retarray[2] is
OK */
2) define your callback to take void **cbretarray, void *new_element,
int num_elements
3) pass &retarray as cbretarray
4) within the callback:

void *orgmem = *cbretarray; /* in case realloc fails */
*cbretarray = realloc(*cbretarray, (num_elements + 1) * sizeof(struct
foo *));
if (! (*cbretarray) )
; /* handle out of mem; original memory pointed to by orgmem */
else
((struct foo **)*cbretarray)[num_elements] = new_element;
/* applying cast to new_element is unnecessary */

Then declare void *hash_keys(..) and call it as
struct foo ** my_foo_array = hash_keys(..) /* no cast req'd */

You may choose to pass num_elements as a pointer and to increment its
dereferenced value within the callback only if realloc succeeds.

.



Relevant Pages

  • Re: segfault w/ block, but not file scope
    ... void foo{ ... possess by-reference arguments, even ordinary local variables ... The "value" of the array is a pointer to the array's ...
    (comp.lang.c)
  • Re: qsort semantics
    ... must pass a pointer to the first object of the array to be sorted. ... int cmp(const void *a, const void *b) { ... You cast a to "pointer to an array of N char". ...
    (comp.lang.c)
  • Re: Static _Bool initialization
    ... The "&" operator will never yield a null pointer if its operand is ... If foo is an array of more than 42 elements, ... If the compiler (which can ...
    (comp.std.c)
  • Re: pass by Reference/value ???
    ... > void foo ... this would be called "passing by reference". ... foo receives a local copy of s and any changes that it makes to s only ... Since s is a pointer, a variable storing the address of s is a pointer to ...
    (comp.lang.cpp)
  • Re: A little help please
    ... Well I guess it would be if I didn't know I was passing an array of ints ... incrementing the pointer by nBytes I was pointing to next array element.? ... array to a void* using a static cast. ...
    (alt.comp.lang.learn.c-cpp)