问题
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:
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).
回答2:
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)
回答3:
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)
回答4:
You need to cast the void *
pointer before dereferencing it.
((some_type *)(s.data))[s.elements] = *(some_type *)item;
来源:https://stackoverflow.com/questions/21019406/dynamic-array-of-void-pointers