【问题标题】:Dynamic Array of Void Pointers空指针的动态数组
【发布时间】:2014-01-27 22:56:18
【问题描述】:

我正在尝试基于动态数组创建动态集合抽象数据类型。但是,当我尝试将数据添加到数组时,会收到编译器警告和错误,它们是:

警告:取消引用 'void *' 指针 [默认启用]

错误:无效表达式的使用无效

我的代码如下,有问题的行我已经标注了注释

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;
}
}

我对 void 指针进行了研究,但显然我在这里遗漏了一些东西。我会很感激我能得到的任何帮助。感谢阅读并希望回答!

【问题讨论】:

  • 你应该把所有的函数定义都放在这里..似乎成员数据应该是无效的**?

标签: c arrays pointers void-pointers


【解决方案1】:

我一直在努力解决同样的问题。我已经将混淆隔离到我们被教导void** ptr 是一个二维数组的想法。也许它也是一个二维数组,但在大多数情况下,我们想要使用单个指针数组的语法和二维只会混淆问题。以整数为例:

int a; 

a 是一个变量的名称,它引用一个包含 int 的内存块。

int* aPtr;

aPtr 是一个变量的名称,它引用一个拥有size_t 类型的内存块。所有指针都是 size_t 类型,就像所有 int 都是 int 类型一样,浮点数是浮点类型。它持有的 size_t 将被分配一个地址(aPtr = &a,或aPtr = malloc(sizeof(int)))。因为这是一个 int 指针,所以分配的地址将是一个 int 的大小,并且它将存储一个 int。因此,您将地址分配给aPtr。您通过取消引用*aPtr = 10; 为该地址分配一个值,aPtr 指向的内存块您读取通过取消引用int val = *aPtr; 指向的 int 中的值

int** pPtr;

pPtr 是一个变量的名称,它引用一个包含 size_t 的内存块。这个 size_t 中存储的地址是另一个 size_t 内存块的地址,即指针。第二个 size_t bloc 是保存整数地址的指针。这样你就有了:

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 需要取消引用才能访问它指向的指针。具体来说,我们希望指向的指针是bPtr,数组中的第二个元素是*(pPtr + 1)。但是我们想要bPtr 指向的值,而不是bPtr 的地址,所以我们必须取消引用*(pPtr + 1) 因此*(*(pPtr + 1))

这样就可以包含空指针:

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

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

正如一些评论员已经指出的那样,必须将 void 指针转换回类型。因此 aPtrbPtr 作为 void 指针存储在数组中需要转换回 int 指针: (int*)*(vPtr + 1)

【讨论】:

    【解决方案2】:

    首先,我认为您打算在结构中包含一个通用指针数组(void * 的数组),因为您的项目是void *,并且您希望将它们存储为一个数组。也就是说,你想要一个void * 的动态数组,因此,你应该使用void **

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

    当然,您的add 函数需要更新:

    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;
        }
    }
    

    注意realloc 行已更改:您不想重新分配到s.allocated * sizeof(s),您需要s.allocated*sizeof(*s.data),因为您将存储void * 类型的元素(*s.data 的类型是void *,我没有明确写 void * 是为了更容易适应未来可能发生的变化。

    另外,我认为您应该更改函数以接收和返回指向struct SET 的指针,否则,您将始终在结构周围进行复制(请记住,值是通过复制传递的)。

    【讨论】:

    • @MattGrima 不客气。动态数组背后的想法总是相同的。当您想在动态数组中存储T 类型的元素时,您必须声明一个指向T 的指针。在这种情况下,您想将void * 存储在一个动态数组中,因此您需要一个指向void * 的指针,即void **。如果你遵循这个思路,你就不会再犯这个错误了:)
    • 但请记住,当您从集合中弹出这些元素时,如果您想取消引用指针,接收它们的函数必须知道它们的类型。
    【解决方案3】:

    您正在尝试将数据用作数组,但已将其声明为 void *。编译器无法判断您是否打算将数据类型存储到此数组中。

    如果您知道要在“数据”中存储什么样的数据,您可能应该为它声明一个类型。 (如 char *data 或 int *data)

    【讨论】:

    • 我不确定我是否遵循.. 数组背后的想法是它可以存储任何类型的数据。
    • 那么你需要根据你存储在其中的数据类型来适当地转换它。
    【解决方案4】:

    您需要在取消引用之前强制转换 void * 指针。

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

    【讨论】:

    • 我已经将它转换为 char *,并且它可以编译,但是我有点担心这会破坏数组的目的。如果它是这样工作的,那为什么不先为字符串定义数组呢?
    猜你喜欢
    • 2014-01-24
    • 1970-01-01
    • 2017-02-07
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2013-04-06
    • 2012-03-09
    • 2012-01-09
    相关资源
    最近更新 更多