2008-12-04 76 views
4

我试图创建一个函数,它将一个数组作为参数,向它添加值(如果需要,增加它的大小)并返回项的数量。 到目前为止,我有:将动态数组传递给函数C

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中的动态结构吗?我应该实施一个链表来处理这些吗?

回答

10

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

ent a[mSize]; 

这是堆栈上的自动分配。如果你想以后使用这个realloc的(),您将创建在使用堆的malloc(阵列),像这样:

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

从而使图书馆的malloc(因此realloc的()等)知道你的阵列。从外观上看,你可能会混淆C99 variable-length arrays与真实dynamic arrays,所以请务必在尝试解决此问题之前了解其中的差异。但是,真的,如果你正在用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); 

这样,用户不必记得到底如何分配的事或什么大小的数组目前。希望能让你开始。

+0

感谢您的支持!非常有用的信息 - 我将会看你是否可以按照Javier的建议重做我的代码。 如果我有多个不同类型的动态数组(如'ents'数组,'foos'数组等),是否可以创建一组方法来处理它们? – Tom 2008-12-04 17:11:41

+0

你可以用C++中的模板做到这一点,但是在C中没有很好的方式来处理它。你可以通过一组精心设计的宏来实现,但是这并不适用于最漂亮或最易维护的代码。 – tgamblin 2008-12-04 17:15:22

6

尝试对其进行重构,以便传入指向数组的指针,即ent **a。然后您将能够更新阵列新位置上的调用者。

+0

您需要将其与tgamblin的答案相结合才能获得完整的解决方案。 – 2008-12-04 17:16:40

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

因此改变在addValues函数的值在主不会改变的价值。要更改主内容的值,您需要将其引用传递给addValues。此刻,a的值被复制并传递给addValues。传递一个参照本发明的使用:

int addValues (int **a, int mSize) 

并调用它喜欢:

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

addValues,访问的一个元素是这样的:

(*a)[element] 

和重新分配阵列像这样:

(*a) = calloc (...); 
1

这是使用OOP的一个很好的理由。是的,你可以在C上做OOP,如果做得对,它甚至看起来不错。

在你不需要继承或多态性,只是封装和方法的概念这个简单的例子

  • 定义与长度和数据指针的结构。也许是一个元素大小。
  • 写getter/setter函数对指向该结构的指针进行操作。
  • 'grow'函数修改结构中的数据指针,但任何结构指针都保持有效。
0

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

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

0

如前所述,您应该传递指针指针来更新指针值。
但我会建议重新设计,并避免这种技术,在大多数情况下,它可以也应该避免。不知道你到底想要达到什么目的,很难建议其他设计,但我99%确定它可以通过其他方式实现。和Javier悲伤 - 认为面向对象,你将永远得到更好的代码。

1

如果您在主要改变的变量声明为

ent *a = NULL; 

像你通过不释放堆栈分配的数组所设想的代码会工作了。设置为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++; 
} 

我预计,如果当前缓冲区需要调整大小使得原有代码的

size = size * 2; 

不必要的realloc的大多数实现内部分配两倍的内存。

0

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