6

我正在尝试创建一个函数,该函数将数组作为参数,向其添加值(必要时增加其大小)并返回项目数。到目前为止,我有:

int main(int argc, char** argv) {
    int mSize = 10;
    ent a[mSize];
    int n;
    n = addValues(a,mSize);

    for(i=0;i<n;i++) {
       //Print values from a
    }
}

int addValues(ent *a, int mSize) {
    int size = mSize;

    i = 0;

    while(....) { //Loop to add items to array
        if(i>=size-1) { 
            size = size*2;
            a = realloc(a, (size)*sizeof(ent));
        }
        //Add to array
        i++;
    }
    return i;
}

如果 mSize 足够大以容纳数组的所有潜在元素,则此方法有效,但如果需要调整大小,则会出现分段错误。

我也试过:

int main(int argc, char** argv) {
    ...
    ent *a;
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent);
    //usual loop
    ...
}

无济于事。

我认为这是因为当我调用 realloc 时,'a' 的副本指向其他地方 - 如何修改它以使 'a' 始终指向相同的位置?

我这样做对吗?有没有更好的方法来处理 C 中的动态结构?我应该实施一个链接列表来处理这些吗?

4

8 回答 8

11

这里的主要问题是您试图将 realloc 与堆栈分配的数组一起使用。你有:

ent a[mSize];

这是堆栈上的自动分配。如果您想稍后在此使用 realloc(),您将使用 malloc() 在堆上创建数组,如下所示:

ent *a = (ent*)malloc(mSize * sizeof(ent));

这样 malloc 库(以及 realloc() 等)就知道你的数组了。从这点看,您可能会将C99 可变长度数组与真正的动态数组混淆,因此在尝试解决此问题之前,请确保您了解其中的区别。

但是,实际上,如果您正在用 C 编写动态数组,您应该尝试使用 OOP-ish 设计来封装有关您的数组的信息并将其对用户隐藏。您希望将有关数组的信息(例如指针和大小)整合到一个结构中,并将操作(例如分配、添加元素、删除元素、释放等)整合到与您的结构一起使用的特殊函数中。所以你可能有:

typedef struct dynarray {
   elt *data;
   int size;
} dynarray;

您可能会定义一些函数来使用 dynarrays:

// malloc a dynarray and its data and returns a pointer to the dynarray    
dynarray *dynarray_create();     

// add an element to dynarray and adjust its size if necessary
void dynarray_add_elt(dynarray *arr, elt value);

// return a particular element in the dynarray
elt dynarray_get_elt(dynarray *arr, int index);

// free the dynarray and its data.
void dynarray_free(dynarray *arr);

这样用户就不必确切地记住如何分配东西或数组当前的大小。希望这能让你开始。

于 2008-12-04T16:51:15.597 回答
6

尝试对其进行修改,以便传入指向数组指针的指针,即ent **a. 然后,您将能够在数组的新位置更新调用者。

于 2008-12-04T16:50:56.640 回答
1

您正在按值传递数组指针。这意味着:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent); // ...is not the same as this
    //usual loop
    ...
}

所以改变函数中a的addValues值不会改变main中a的值。要更改 a in main 的值,您需要将对它的引用传递给addValues. 目前, a 的值正在被复制并传递给addValues。传递对使用的引用:

int addValues (int **a, int mSize)

并称之为:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
    addValues (&a, mSize);
}

在 中addValues,像这样访问 a 的元素:

(*a)[element]

并像这样重新分配数组:

(*a) = calloc (...);
于 2008-12-04T16:54:45.073 回答
1

这是使用 OOP 的一个很好的理由。是的,您可以在 C 上执行 OOP,如果操作正确,它甚至看起来不错。

在这个简单的情况下,您不需要继承也不需要多态性,只需要封装和方法概念:

  • 定义一个具有长度和数据指针的结构。也许是元素大小。
  • 编写对指向该结构的指针进行操作的 getter/setter 函数。
  • 'grow' 函数修改结构中的数据指针,但任何结构指针都保持有效。
于 2008-12-04T16:57:31.743 回答
1

如果您将 main 中的变量声明更改为

ent *a = NULL;

通过不释放堆栈分配的数组,代码将更像您想象的那样工作。将 a 设置为 NULL 是可行的,因为 realloc 将其视为用户调用了 malloc(size)。请记住,通过此更改, addValue 的原型需要更改为

int addValues(ent **a, int mSize)

并且代码需要处理 realloc 失败的情况。例如

while(....) { //Loop to add items to array
    tmp = realloc(*a, size*sizeof(ent));
    if (tmp) {
        *a = tmp;
    } else {
        // allocation failed. either free *a or keep *a and
        // return an error
    }
    //Add to array
    i++;
}

如果当前缓冲区需要调整大小以制作原始代码,我希望 realloc 的大多数实现将在内部分配两倍的内存

size = size * 2;

不必要。

于 2008-12-04T21:03:16.680 回答
0

Xahtep 解释了调用者如何处理 realloc() 可能将数组移动到新位置的事实。只要你这样做,你应该没问题。

如果您开始使用大型数组, realloc() 可能会变得昂贵。那时是开始考虑使用其他数据结构的时候了——链表、二叉树等。

于 2008-12-04T16:58:25.330 回答
0

如前所述,您应该将指针传递给指针以更新指针值。
但我建议重新设计并避免这种技术,在大多数情况下可以而且应该避免。在不知道您到底想要实现什么的情况下,很难提出替代设计,但我 99% 确信它可以通过其他方式实现。正如Javier悲伤的那样——考虑面向对象,你总会得到更好的代码。

于 2008-12-04T17:02:24.933 回答
0

你真的需要使用C吗?这将是 C++ 的“std::vector”的一个很好的应用,它恰好是一个动态大小的数组(只需一次调用即可轻松调整大小,您不必自己编写和调试)。

于 2008-12-06T00:15:42.747 回答