我有一个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;
}
但这似乎不对...... 任何想法?
答案 0 :(得分:3)
一些建议:
groupCount % INCREMENT == 0
取代groupCount == arraySize
,这在我看来更直观。log_2 n
个数量的reallocs,n
是最终的数组大小,你最多浪费一半的内存(内存通常很便宜,就像我说的那样取决于在申请上)。如果这浪费了很多记忆,你可以说1.5。如果您想要更详细的解释,我建议this Joel on Software文章,关于realloc的部分约为2/3。更新
其他一些事情:
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
)有两个成员(bar1
和bar2
),另一个结构是一个数组包装器(foo_array_t
)。 foo_array_t
跟踪数组的当前大小和数组中的元素数。它有一个add element函数(foo_array_add
)。请注意,有foo_init
和new_foo
,foo_init
获取指向foo_t
的指针而new_foo
没有,而是返回指针。所以foo_init
假定已经以某种方式分配了内存,堆,堆栈或其他什么并不重要,而new_foo
将从堆中分配内存。还有一个名为LESS_MALLOCS
的预处理宏。这会更改foo_array_t
数组成员的定义,初始数组分配的大小,重新分配期间的大小以及是否使用foo_init
或new_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))
第二种方法可能需要与构造函数不同的初始化例程,因为已经分配了内存