C使用构造函数创建struct数组

时间:2015-06-13 16:07:31

标签: c arrays struct

我有一个C结构:

list.set()

它有一个像这样的构造函数:

typedef struct {
  Dataset *datasets;
  int nDatasets;
  char *group_name;      
  enum groupType type;  
} DatasetGroup; 

我想动态创建这些结构的数组。这是最好的方法吗?

到目前为止,我有类似的内容:

DatasetGroup * new_DatasetGroup(char *group_name, enum groupType type, enum returnCode *ret)
{
    DatasetGroup *dg;
    dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
    if (dg == NULL)
    {
     *ret = EMEMORY_ERROR;
    }

    // Allocate space for a few datasets
    dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
    if (dg->datasets == NULL)
    {
        *ret = EMEMORY_ERROR;
    }
    dg->group_name= malloc(sizeof(char) * strlen(group_name));
    strcpy(dg->group_name, group_name);
    dg->type = type;
    groupCount++;
    return dg;
  }

但这似乎不对...... 任何想法?

2 个答案:

答案 0 :(得分:3)

一些建议:

  1. 您的groupCount由struct的构造函数递增。这意味着您只能拥有一个使用数组函数的结构数组。我建议让阵列负责管理计数。
  2. 为了影响你是否想拥有一个托管数组,我会为它创建一个结构,让它同时保存指向数组的指针,对象的数量和数组的大小(例如结构的最大数量)目前可以举行)
  3. 如果你正确跟踪你拥有的元素数量和数组的大小,你可以用groupCount % INCREMENT == 0取代groupCount == arraySize,这在我看来更直观。
  4. 您可以通过使数组是元素数组而不是指针数组来避免构造函数中的第二个malloc。构造函数只是初始化struct成员而不是分配内存。如果你这么做很多,你将避免大量的内存碎片。
  5. 最后,虽然这取决于您的应用程序,但我通常建议您在realloc时不要增加常量而不是当前数组大小的倍数。如果说你将数组大小增加一倍,你只需要log_2 n个数量的reallocs,n是最终的数组大小,你最多浪费一半的内存(内存通常很便宜,就像我说的那样取决于在申请上)。如果这浪费了很多记忆,你可以说1.5。如果您想要更详细的解释,我建议this Joel on Software文章,关于realloc的部分约为2/3。
  6. 更新

    其他一些事情:

    dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
    if (dg == NULL)
    {
     ret = EMEMORY_ERROR;
    }
    
    // Allocate space for a few datasets
    dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
    

    如前所述非常糟糕,因为即使它是NULL,我们也会这样做。您可能希望在检测到错误后立即退出。

    此外,您正在设置ret但是ret按值传递,因此如果被调用者更改了调用者,则不会更改调用者。相反,你可能想传递指针并取消引用它。

    更新2:我能举一个例子,当然,快点不要那么多;-D。

    考虑以下代码(如果有任何错误,我道歉,仍然半睡半醒):

    #include <stdio.h>
    #include <stdlib.h>
    
    #define LESS_MALLOCS
    
    #define MAX_COUNT 100000000
    
    typedef struct _foo_t
    {
      int bar1;
      int bar2;
    } foo_t;
    
    void foo_init(foo_t *foo, int bar1, int bar2)
    {
      foo->bar1 = bar1;
      foo->bar2 = bar2;
    }
    
    foo_t* new_foo(int bar1, int bar2)
    {
      foo_t *foo = malloc(sizeof(foo_t));
      if(foo == NULL) {
        return NULL;
      }
      foo->bar1 = bar1;
      foo->bar2 = bar2;
      return foo;
    }
    
    typedef struct _foo_array_t
    {
    #ifdef LESS_MALLOCS
      foo_t *array;
    #else
      foo_t **array;
    #endif
      int count;
      int length;
    } foo_array_t;
    
    void foo_array_init(foo_array_t* foo_array, int size) {
      foo_array->count = 0;
    #ifdef LESS_MALLOCS
      foo_array->array = malloc(sizeof(foo_t) * size);
    #else
      foo_array->array = malloc(sizeof(foo_t*) * size);
    #endif
      foo_array->length = size;
    }
    
    int foo_array_add(foo_array_t* foo_array, int bar1, int bar2)
    {
      if(foo_array->count == foo_array->length) {
    #ifdef LESS_MALLOCS
        size_t new_size = sizeof(foo_t) * foo_array->length * 2;
    #else
        size_t new_size = sizeof(foo_t*) * foo_array->length * 2;
    #endif
        void* tmp = realloc(foo_array->array, new_size);
    
        if(tmp == NULL) {
          return -1;
        }
        foo_array->array = tmp;
        foo_array->length *= 2;
      }
    #ifdef LESS_MALLOCS
      foo_init(&(foo_array->array[foo_array->count++]), bar1, bar2);
    #else
      foo_array->array[foo_array->count] = new_foo(bar1, bar2);
      if(foo_array->array[foo_array->count] == NULL) {
        return -1;
      }
      foo_array->count++;
    #endif
      return foo_array->count;
    }
    
    
    int main()
    {
      int i;
      foo_array_t foo_array;
      foo_array_init(&foo_array, 20);
      for(i = 0; i < MAX_COUNT; i++) {
        if(foo_array_add(&foo_array, i, i+1) != (i+1)) {
          fprintf(stderr, "Failed to add element %d\n", i);
          return EXIT_FAILURE;
        }
      }
    
      printf("Added all elements\n");
      return EXIT_SUCCESS;
    }
    

    有一个结构(foo_t)有两个成员(bar1bar2),另一个结构是一个数组包装器(foo_array_t)。 foo_array_t跟踪数组的当前大小和数组中的元素数。它有一个add element函数(foo_array_add)。请注意,有foo_initnew_foofoo_init获取指向foo_t的指针而new_foo没有,而是返回指针。所以foo_init假定已经以某种方式分配了内存,堆,堆栈或其他什么并不重要,而new_foo将从堆中分配内存。还有一个名为LESS_MALLOCS的预处理宏。这会更改foo_array_t数组成员的定义,初始数组分配的大小,重新分配期间的大小以及是否使用foo_initnew_foo。必须更改数组及其大小以反映指针或实际元素是否在数组中。定义LESS_MACRO时,代码遵循我对4号的建议,否则,它与您的代码更相似。最后,main包含一个简单的微基准。结果如下:

    [missimer@asus-laptop tmp]$ gcc temp.c # Compile with LESS_MACROS defined
    [missimer@asus-laptop tmp]$ time ./a.out 
    Added all elements
    
    real    0m1.747s
    user    0m1.384s
    sys     0m0.357s
    [missimer@asus-laptop tmp]$ gcc temp.c #Compile with LESS_MACROS not defined 
    [missimer@asus-laptop tmp]$ time ./a.out 
    Added all elements
    
    real    0m9.360s
    user    0m4.804s
    sys     0m1.968s
    

    并非time是测量基准测试的最佳方法,但在这种情况下,我认为结果不言而喻。此外,当您分配元素数组而不是指针数组然后分别分配元素时,您将减少必须检查错误的位置数。当然一切都有权衡,如果例如结构非常大并且你想在数组中移动元素你会做很多memcpy - 而不是仅仅在你的指针中移动指针做法。

    另外,我建议不要这样做:

    dg_array = realloc(dg_array, sizeof(DatasetGroup) * (groupCount + INCREMENT));
    

    如果realloc失败并返回NULL,则丢失原始指针的值。也像你以前的ret一样,你应该传递一个指针而不是值,因为你没有将值更改为调用者,只是被调用者然后退出所以它没有真正的影响。最后,我注意到你改变了你的函数定义以获得一个指向ret的指针,但是当你使用它时需要取消引用该指针,当你尝试使用它时,你应该得到编译器警告(甚至可能是错误)。

答案 1 :(得分:1)

你可以做两件事,要么动态创建一个struct指针数组,然后调用你的新函数来创建N个数据组,要么你可以一次动态地为N个结构请求内存,这就意味着你的N个结构会是连续的分配。

Datagroup **parry = malloc(sizeof(datagroup *) * N)
for (int i = 0; i < N; i++){
    parry[i] = //yourconstructor
}

或者

//allocate N empty structures
Datagroup *contarr = calloc(N, sizeof(Datagroup))

第二种方法可能需要与构造函数不同的初始化例程,因为已经分配了内存