4

I'm trying to create a dynamic set abstract data type, based on a dynamic array. However, I get a compiler warning and an error when I try to add the data to the array, which are:

warning: dereferencing 'void *' pointer [enabled by default]

error: invalid use of void expression

My code is as follows, I've marked the problematic line with a comment

struct SET 
{
//general dynamic array
void *data;
int elements; //number of elements
int allocated; // size of array
};

struct SET create()
{
//create a new empty set

struct SET s;
s.data = NULL;
s.elements = 0;
s.allocated = 0;  //allocations will be made when items are added to the set    
puts("Set created\n");
return s;
}

struct SET add(struct SET s, void *item)
{
//add item to set s

if(is_element_of(item, s) == 0) //only do this if element is not in set
{
    if(s.elements == s.allocated)  //check whether the array needs to be expanded
    {   
        s.allocated = 1 + (s.allocated * 2);  //if out of space, double allocations
        void *temp = realloc(s.data, (s.allocated * sizeof(s))); //reallocate memory according to size of the set

        if(!temp) //if temp is null 
        {
            fprintf(stderr, "ERROR: Couldn't realloc memory!\n");
            return s;
        }

        s.data = temp;
    }

    s.data[s.elements] = item;   //the error is here
    s.elements = s.elements + 1;
    puts("Item added to set\n");

    return s;
}

else
{
    fprintf(stdout, "Element is already in set, not added\n");
    return s;
}
}

I've done research on void pointers, but clearly I'm missing something here. I'd appreciate any help I can get. Thanks for reading and hopefully answering!

1
  • you should put all the function definition here.. it seem the member data should be void** ? Commented Jan 9, 2014 at 12:04

4 Answers 4

4

First, I think what you intend to have in your structure is an array of generic pointers (array of void *), because your items are void *, and you want to store them as an array. That is, you want a dynamic array of void *, thus, you should use void **:

struct SET {
    void **data;
    int elements;
    int allocated;
};

Of course, your add function needs to be updated:

struct SET add(struct SET s, void *item) {    
        if (is_element_of(item, s) == 0) {
            if (s.elements == s.allocated) {   
                s.allocated = 1 + (s.allocated * 2);
                void **temp = realloc(s.data, (s.allocated * sizeof(*s.data)));
                if (!temp) {
                    fprintf(stderr, "ERROR: Couldn't realloc memory!\n");
                    return s;
                }

            s.data = temp;
        }
        s.data[s.elements] = item;
        s.elements = s.elements + 1;
        puts("Item added to set\n");
        return s;
    }

    else {
        fprintf(stdout, "Element is already in set, not added\n");
        return s;
    }
}

Note the realloc line was changed: you don't want to realloc to s.allocated * sizeof(s), you want s.allocated*sizeof(*s.data), since you'll be storing elements of type void * (the type of *s.data is void *, I didn't explicitly write void * to make it easier to accomodate possible future changes).

Also, I believe you should change your functions to receive and return pointers to struct SET, otherwise, you will always be copying around the structure (remember that values are passed by copy).

Sign up to request clarification or add additional context in comments.

2 Comments

@MattGrima You're welcome. The idea behind dynamic arrays is always the same. When you want to store elements of type T in a dynamic array, then you must declare a pointer-to-T. In this case, you wanted to store void * in a dynamic array, so you need a pointer to void *, that is, void **. If you follow this line of thought, you will never make this mistake again :)
Bear in mind, though, that when you pop out those elements from the set, the function receiving them must know their type if you want to dereference the pointer.
1

You are trying to use data as an array, but have declared it as void *. The compiler is not able to figure out if you intend to store the type of data into this array.

If you know what kind of data you are going to store in 'data', you should probably declare a type for it. (like char *data or int *data)

2 Comments

I'm not sure I follow.. The idea behind the array is that it can store data of any type.
Then you need to cast it appropriately based on the type of data you are storing in it.
1

I have been struggling with the same issue. I have isolated the confusion to to the idea we are taught that void** ptr is a two dimensional array. Maybe it is also a two dimensional array, but for the most parts we want to use the syntax for a single array of pointers and two dimensionality just confuses the issue. To use ints for example:

int a; 

a is the name of a variable that refers to a bloc of memory that holds an int.

int* aPtr;

aPtr is the name of a variable that refers to a bloc of memory that holds a size_t type. All pointers are size_t types just as all ints are int types, floats are float types. The size_t it holds will be assigned an address (aPtr = &a, or aPtr = malloc(sizeof(int))). Because this is an int pointer the assigned address will be the size of an int, and it will store an int. Thus you assign an address to aPtr. You assign a value to this address, the memory bloc aPtr points to, by dereferencing *aPtr = 10; You read the value in the int pointed to by dereferencing int val = *aPtr;

int** pPtr;

pPtr is the name of a variable that refers to a bloc of memory that holds a size_t. The address stored in this size_t is the address of another size_t bloc of memory, i.e., a pointer. This second size_t bloc is the pointer that will hold the address of the integer. Thus you have:

int a = 10;
int* aPtr = &a;
int* bPtr = malloc(sizeof(int));
*bPtr = 20;
int** pPtr = malloc(2 * sizeof(int*));
pPtr[0] = aPtr;
*(pPtr + 1) = bPtr;

printf("a is %d, aPtr address is %d, aPtr points to %d, bPtr points to %d.\n", a, aPtr, *aPtr, *bPtr);
printf("pPtr[0] points to aPtr which points to %d. Which is to say, qwe dereference pPtr to get to aPtr and dereference aPtr to get to its value.\n", *(pPtr)[0]);
printf("Which can be handled with different syntax illustrated by pPtr[1] to give %d\n", *(*(pPtr + 1)) );

pPtr needs to be dereferenced to access the pointer it points to. Specifically we want the pointer pointed to to be bPtr, the second element in the array so *(pPtr + 1). But we want the value that bPtr points to, not bPtr's address so we have to dereference *(pPtr + 1) hence *(*(pPtr + 1))

Void pointers can be included thusly:

void** vPtr = malloc(2 * sizeof(void*));
*(vPtr + 0) = aPtr;
*(vPtr + 1) = bPtr;

printf("vPtr[1] is %d\n", *((int*)*(vPtr + 1)));

As several commentators have have already noted the void pointer must be cast back to type. Thus aPtr and bPtr which were stored in the array as void pointers need to be cast back into int pointers: (int*)*(vPtr + 1)

Comments

0

You need to cast the void * pointer before dereferencing it.

((some_type *)(s.data))[s.elements] = *(some_type *)item;

1 Comment

I've cast it to char *, and it compiles, however I'm slightly worried that this defeats the purpose of the array. If it works like this, then why not have the array defined for strings to begin with?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.