【问题标题】:Dynamic array with void pointer带空指针的动态数组
【发布时间】:2014-01-24 07:29:46
【问题描述】:

所以学习 C 中的指针,我认为作为练习,我可以做一些通用数组,并且在像这样使用 void** 时我得到了它:

struct array{
    void **data;
    size_t size, capacity;
};

像这样插入元素:

void array_append(array *a, void *element){
    if(a->size == a->capacity){
        a->capacity += ARRAY_GROW_CONSTANT;
        a->data = realloc(a->data, sizeof(void*)*a->capacity);
    }
    a->data[a->size++] = element;
}

但这不是很好。数组存储指向元素的指针,因此当元素的范围结束时,它变得无效,并且它使数组的内容分散在整个内存中。我认为这可以通过分配元素本身来解决

a->data[a->size++] = element;

我会做类似的事情

a->data[a->size] = malloc(inserted_element_size);
memcpy(a->data[a->size], &element, inserted_element_size);
size++;

但我认为在使用普通 void* 而不是 void** 时我可以获得相同的功能

struct array{
    void *start;
    size_t element_size, size;
};

并插入类似的元素

void array_append(array *a, void *element){
    a->size += 1;
    a->data = realloc(a->data, (a->size*a->element_size));
    memcpy(a->data + (a->size - 1)*a->element_size, &element, a->element_size);
}

但这会导致段错误,我不知道为什么。据我了解(显然我不了解),指针是内存中的地址,所以如果我有一个连续的内存块,我可以使用偏移量在其中存储任何类型的变量。

编辑:感谢您的解释,它真的很有帮助。

a->数据初始化为什么?

我使用了一个函数来初始化数组,a->data 被初始化为 element_size。

调用者必须将结果转换为元素 *

我认为我可以使用宏来缩短打字时间(我认为这是一件坏事?),但我不知道从 void* 到 struct* 的类型转换性能。

对我来说,直接创建元素的动态数组似乎更实用。

但这不允许我将数组用作通用数组?我想要的是定义一个通用数组,我可以用它来存储任何类型,比如

array *a1 = create_array(sizeof(int)); // array of int
array *a2 = create_array(sizeof(double)); // array of double
etc...

为什么要将数据存储在一个连续的块中?

因为我认为您需要一个连续的内存块才能使用带有偏移量的 memcpy。

【问题讨论】:

    标签: c pointers void-pointers pointer-arithmetic


    【解决方案1】:

    使用 void* 作为数据数组对代码的最后部分进行注释。代码应该可以工作,但有问题:

    您传递的是元素指针的地址,而不仅仅是指针本身,它已经指向正确(希望)数据。

    您也不能对 void 进行指针运算,但某些编译器允许这样做。

    memcpy 的正确版本应该是

    memcpy ( ( unsigned char* )a->data + (a->size - 1)*a->element_size, element, a->element_size);
    

    【讨论】:

      【解决方案2】:

      a->data 初始化为什么?为此,应在创建(空)数组时将其设置为 NULL。

      此外,您的地址计算不考虑指针算术。 a->data 是一个指针(指向 void *),因此 (a->size - 1)*a->element_size 偏移量将乘以指针的大小(指向 void *)。 将a->data 设置为void * 会导致编译器错误,因为void 没有大小。

      如果你真的想这样做,最好将a->data声明为char *,保证大小为1。

      注意:访问您的数组需要转换为 (element*)。这将阻止您使用方括号。
      您将必须提供一个访问器功能,例如 void * array_at(size_t index) { return &a->data[index*a->element_size]; }
      然后调用者必须将结果转换为element *。

      直接创建elements 的动态数组对我来说似乎更实用。
      如果你愿意,你仍然可以拨打realloc。

      但我想到的第一个问题是:为什么要将数据存储在连续的块中?

      这并不像您想象的那样节省内存,因为重复调用 realloc() 会加重内存分配器的压力,浪费时间进行复制,甚至可能比单个 mallocs 的集合更碎片化堆。

      【讨论】:

        猜你喜欢
        • 2014-01-27
        • 1970-01-01
        • 1970-01-01
        • 2013-12-02
        • 2017-02-07
        • 1970-01-01
        • 2023-03-17
        • 2014-05-22
        • 1970-01-01
        相关资源
        最近更新 更多