27

I am trying to use a double void pointer but I am a little bit confused about the usage. I have a struct that contains a void ** array.

struct Thing{
    void ** array;
};

struct Thing * c = malloc (sizeof(struct Thing));
c->array = malloc( 10 * sizeof(void *) );

So If I want to assign a different object to each pointer and try to retrieve the value

 // Option 1
 *(c->array + index) = (void *) some_object_ptr;

 // Option 2
 c->array[index] = (void *) some_object_ptr;

then, I have another function that gives (void *) item that points to each cell, not the some_object_ptr.

If I want to retrieve the value which pointed to by some_object_ptr,
should I do

 function return type is 'void *' and takes argument 'void *'
 // Option 3 
 return (void**) item

 // Option 4
 return *((void**)item)?

the weird thing is that when I used array the array subscript method I couldn't use option 4, only option 3; and when I used *(c->array + index) I could only use opt.4. and not opt.3. ..

Can anyone please tell me about this? If I am making any invalid assumptions, then could you please correct me?

Kevin
  • 53,822
  • 15
  • 101
  • 132
in His Steps
  • 3,075
  • 6
  • 30
  • 38
  • 11
    Maybe he needs it? –  Jan 27 '12 at 22:29
  • Also, options 3 and 4 are not the same, 3 returns a `void**` and 4 returns a `void*`. And what exactly is `item`? – Kevin Jan 27 '12 at 22:30
  • 1
    "then, I have another function that gives (void * item) that points to each cell, not the 'some_object_ptr'." <- what kinda cell? It's returning the address of some_object_ptr? –  Jan 27 '12 at 22:31
  • at Kevin, sorry I just edited – in His Steps Jan 27 '12 at 22:32
  • so assume it is a function that returns poinere to the object that is pointed by some_object_ptr and it takes void * as an argument that points to the cell of array – in His Steps Jan 27 '12 at 22:33

4 Answers4

41

A void ** is just a pointer to a pointer to memory with an unspecified type. You can only dereference it once (since you can't dereference a void *). However, apart from that, it is basically like any other pointer type. If it helps you, think of it the same way as you would with int *.

So, in your specific situation, we have:

void** array;
int arrayLen = 10;
array = (void**)malloc(arrayLen * sizeof(void*));

some_type_t* some_object_ptr;    
// The following two assignment are equivalent since in C,
// array[index] <=> *(array + index)
array[index] = (void*)some_object_ptr;
*(array + index) = (void*)some_object_ptr;

Then array is a pointer to the whole array, while *array is a pointer to the first element, since it is equivalent to array[0].

Eitan T
  • 32,660
  • 14
  • 72
  • 109
Sylvain Defresne
  • 42,429
  • 12
  • 75
  • 85
  • It depend what you want to return. Do you want to return the first element in the array or the array. If you had an `int* array;`, would you do `return array;` or `return array[0];`? This will be the same here. – Sylvain Defresne Jan 27 '12 at 22:38
  • I want to return what is in the cell, which I think is the void pointer that points to another object and returning part is in different function that it does not know the index. Instead it is given void * item which points to the cell that contains the void pointer – in His Steps Jan 27 '12 at 22:42
  • Don't forget to check if the malloc was actually valid. right after the assignment, you should check with something like this: `if(array == NULL) return;` – Nick Cuevas Apr 06 '18 at 22:24
2

One quick hint about pointers: if you are casting it, you are probably doing something wrong.

As for your question. I am not sure what item is in your problem. In your first part you've already discovered how to acces a member in your array. You could simply use it:

void *get_item(Thing *c, int index)
{
    return *(c->array + index); // or return c->array[index];
}

If you need the address of the pointer at index:

void **get_item_cell(Thing *c, int index)
{
    return c->array + index; // or return &c->array[index];
}

In the second part, you don't dereference the pointer (for + option), or take the address of array result, since it automatically dereferences it.


EDIT: I think I now know what you want. You have a function similar to my second one above, but it is:

void *get_item_cell(Thing *c, int index)
{
    return (void *)(c->array + index);
}

You want to dereference the value returned from this function, and access the object. In that case, you can only use Option 4 safely. Since you don't have the index, you cannot move to any other cell (you don't know if you are at the end of the array, or at the beginning - so no additions or subtractions). You can only fix the mistake of the above function: cast to void **, and then dereference it: *(void **)item. This will give you a void *. If you want to access the object pointed from this cell, you need to cast that to the correct type as well: some_object_ptr *obj = *(void**)item.

vhallac
  • 13,301
  • 3
  • 25
  • 36
  • oh, so returning part is in different function that it does not know the index and void * item points to the array of cell – in His Steps Jan 27 '12 at 22:38
  • Only my understanding of what you want to do. If you have the address of the pointee (an object in the array), and want to return to either the index or the cell of the array, you'll have to loop through each cell, check if the pointee is the same object as the one you want, then return the relevant information when it is. If this is what you want, I can add that to my answer. – vhallac Jan 27 '12 at 22:40
1

The fact you are working with void* and void** doesn't matter, pointer arithmetic still works fine, so both options you wrote are correct.

Here's an example:

struct Thing
{
    void ** array;
};

struct Object
{
    int i;
    char c;
};

int main ()
{

    struct Thing * c = malloc (sizeof(struct Thing));
    c->array = malloc(10 * sizeof(void*));

    struct Object * o = malloc (sizeof(struct Object));
    o->i = 2; o->c = 'a';

    *(c->array + 2) = o;

    printf("Object: i = %d, c = %c\n", ((Object*)c->array[2])->i, ((Object*)c->array[2])->c);

    free(o);
    free(c->array);
    free(c);
    return 0;
}

Since it's void* you can put there pointer to whatever, just don't forget it to cast to original type before using it ;)

LihO
  • 41,190
  • 11
  • 99
  • 167
  • 4
    You DO NOT have to cast malloc()'s return value. C standard explicitly says that void * is compatible with *any* pointer type. Several respectful C programmers, for example Linus Torvalds, and enterprises such as GNU argue against this casting and call it bad practice as it decreases readability. –  Jan 27 '12 at 22:38
  • 1
    Also, pointer arithmetic is NOT the same as with integers. AFAIK GCC assumes void * to be a byte ptr, and, for example, on a 32-bit system, int is 4 bytes, so (int *)ptr + 1 will give a different memory address from that of (void *)ptr + 1 would. –  Jan 27 '12 at 22:41
  • Sorry, I'm used to work with Microsoft Visual Studio even when I code in C. Compiler won't let me write malloc without casting it's return value... I edited my answer – LihO Jan 27 '12 at 22:45
  • 1
    @user529758 I say http://bigthink.com/videos/why-the-programming-language-c-is-obsolete – user877329 Jan 03 '17 at 10:07
-1

Almighty push!

any_type x ;
void * v = * ( void * * ) & x ;

almighty pull!

void * v ;
any_type x = * ( any_type * ) & v ;

beware of losing/gaining digits

Fabio
  • 211
  • 1
  • 8
  • What purpose does `void *v = *(void **) &x;` serve beyond `void *v = &x;`? The cast and dereference of `&x` does nothing. A *memory address* has no type to begin with. – David C. Rankin Mar 06 '16 at 10:30
  • @DavidC.Rankin: One could trick around this way C's limitation not allowing to assign a function-pointer's value to a `void`-pointer. – alk Mar 06 '16 at 10:59
  • Just a fun with * ( void * * ) & , a tricky way to cast any type into a void* and viceversa... nothing serious wrt the question, I've deserved the -1 :) – Fabio Mar 06 '16 at 16:38
  • @Fabio This is undefined behavior and could make your computer grow legs and crawl away. Use `memcpy` for implementation-defined behavior, which is partially better. – S.S. Anne Sep 12 '19 at 20:21