为不同大小的结构数组动态分配空间

时间:2018-04-13 15:42:05

标签: c multidimensional-array memory-management dynamic allocation

我面临的问题对我来说似乎很复杂,所以我会非常感谢谁能帮助我。

我试图仅使用结构复制字符串行为数组 所以没有例如

char **mys={"hello","this is a long message"};

我正在尝试使用带有结构的数组(每个结构包含一个可变长度的数组)。我不太确定我是否很好地暴露了我的问题所以我希望代码能够表明我正在尝试做什么:

我的两个结构:

typedef struct
{
    int *iTab;
    int iTail;
}strDynArr;

typedef struct
{
    strDynArr **iTab;
    int iTailStruct;
    int iTail;
}strDynDynArr;

与这两种结构相关的所有功能:

void initArray(strDynArr *ar)
{
    ar->iTab=malloc(sizeof(int));
    ar->iTail=1;
}

void pushArray(int iElement,strDynArr *ar)
{
    ar->iTab[ar->iTail-1]=iElement;
    realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}

void freeDynArray(strDynArr *ar)
{
    free(*ar->iTab);
}

void initDynDynArray(strDynDynArr *ar)
{
    ar->iTab=malloc(sizeof(int));
    ar->iTail=1;
    ar->iTailStruct=1;
}

//the problem
void pushDynDynArray(strDynArr *daElement,strDynDynArr *ar)
{
    //ar->iTab[ar->iTail-1]=daElement;
    ar->iTailStruct+=(daElement->iTail+1);
    realloc(ar->iTab,(ar->iTailStruct)*sizeof(int));
    ar->iTab[ar->iTailStruct-(daElement->iTail+1)]=daElement;
    //realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}
void freeDynDynDynArray(strDynDynArr *ar)
{
    free(*ar->iTab);
}

我遇到的一个函数是pushDynDynArray到目前为止,我已经尝试了两件事,或者只是使用指向结构strDynArr的指针,但我不知道如何管理空间,所以我试图分配结构strDynDynArr数组中包含的所有结构的大小。

所以我想知道的是如何为我的数组(iTab)为strDynDynArr类型的结构分配空间。

换句话说,我存储多个包含例如的结构的方式是什么:

//content of structure 1
int *iTab={2,3,4};
int iTail=3;

//content of structure 2
int *iTab={5,8,9,10,54,8,2};
int iTail=7;

欢迎所有帮助,非常感谢!

1 个答案:

答案 0 :(得分:1)

为简单起见,我们将每个“字符串”数组调用一个表(数组),每个“字符串”调用一个项目数组。在您的情况下,项目类型似乎是int

typedef int  item;

typedef struct {
    size_t  size;   /* Number of items allocated for */
    size_t  used;   /* Number of items in item[] */
    item   *item;
} array;

typedef struct {
    size_t  size;   /* Number of array (pointers) allocated for */
    size_t  used;   /* Number of arrays in array[] */
    array  *array;
} table;

为这些类型定义初始化宏和函数是很常见的:

#define  ARRAY_INIT  { 0, 0, NULL }
#define  TABLE_INIT  { 0, 0, NULL }

static inline void array_init(array *ref)
{
    if (!ref) {
        fprintf(stderr, "array_init(): NULL array!\n");
        exit(EXIT_FAILURE);
    }
    ref->size = 0;
    ref->used = 0;
    ref->item = NULL;
}

static inline void table_init(table *ref)
{
    if (!ref) {
        fprintf(stderr, "table_init(): NULL table!\n");
        exit(EXIT_FAILURE);
    }
    ref->size = 0;
    ref->used = 0;
    ref->array = NULL;
}

此外,免费功能显然很有用:

static inline void array_free(array *ref)
{
    if (!ref) {
        fprintf(stderr, "array_free(): NULL array!\n");
        exit(EXIT_FAILURE);
    }

    free(ref->item); /* Note: free(NULL) is safe; it does nothing. */
    ref->size = 0;
    ref->used = 0;
    ref->item = NULL;
}

static inline void table_free(table *ref)
{
    size_t  i;

    if (!ref) {
        fprintf(stderr, "table_free(): NULL table!\n");
        exit(EXIT_FAILURE);
    }

    i = ref->size;
    while (i-->0)
        array_free(ref->array + i); /* array_free(&(ref->array[i])); */

    free(ref->array);
    ref->size = 0;
    ref->used = 0;
    ref->array = NULL;
}

当释放表时,我们想要单独释放所有(可能的)数组,然后释放指针使用的内存。请注意,可以假设只有used个数组正在使用中;但是,我希望上面的内容是彻底的,并且释放了为。

分配的所有size数组

上面的init和free函数都有一个指向数组或表的指针。它们永远不应该是NULL。 (也就是说,结构中的itemarray 成员可能是NULL;只是你永远不应该调用例如array_init(NULL);或{{1} }。)

让我们实现一个函数来从一个单独的数组(可能是也可能不是表的一部分)推送和弹出单个int:

table_free(NULL)

和相应的弹出操作:

void array_push(array *ref, const item value)
{
    if (!ref) {
        fprintf(stderr, "array_push(): NULL array!\n");
        exit(EXIT_FAILURE);
    }

    /* Need to grow the array? */
    if (ref->used >= ref->size) {
        size_t  size; /* Minimum is ref->used + 1 */
        void   *temp;

        /* Dynamic growth policy. Pulled from a hat. */
        if (ref->used < 64)
            size = 64;  /* Minimum 64 elements */
        else
        if (ref->used < 1048576)
            size = (3*ref->used) / 2; /* Grow by 50% up to 1048576 */
        else
            size = (ref->used | 1048575) + 1048577; /* Grow to next multiple of 1048576 */

        temp = realloc(ref->item, size * sizeof ref->item[0]);
        if (!temp)
            return -1; /* Out of memory */

        ref->item = temp;
        ref->size = size;
    }

    /* Append value to array. */
    ref->item[ref->used++] = value;
}

在您的程序中,您可以使用数组,例如:

item array_pop(array *ref)
{
    if (!ref) {
        fprintf(stderr, "array_pop(): NULL array!\n");
        exit(EXIT_FAILURE);
    }

    if (ref->used < 1) {
        fprintf(stderr, "array_pop(): Array is already empty; nothing to pop.\n");
        exit(EXIT_FAILURE);
    }

    /* Since ref->used is the *number* of items in the array,
       we want to decrement it first, to get the last item in array. */
    return ref->item[--ref->used];
}

array scores = ARRAY_INIT; array_push(&scores, 5); array_push(&scores, 2); printf("%d\n", array_pop(&scores)); /* Will print 2 */ printf("%d\n", array_pop(&scores)); /* Will print 5 */ array_free(&scores); 都声明并初始化数组(到空数组)。您也可以等效地使用array scores = ARRAY_INIT;

array scores; array_init(&scores);中的调整大小或增长政策大致与我个人推荐的一致,尽管实际数值是从帽子中提取的,您可能希望调整它们。这个想法是为(例如,64)分配了最少数量的项目。对于较大的数组,我们会逐渐增加大小,以便在数组较大时,大小增加也很大。许多人使用100%的尺寸增加(尺寸加倍,即array_push()),但我更喜欢50%的增加(将尺寸乘以一,一半,size = 2 * ref->used;)。对于大型阵列,我们不想浪费大量内存,因此我们分配固定大小(但很大)的块。 (没有必要或真正有益于将巨大的大小与我所做的那样对齐;我只是喜欢它。而更复杂的代码确保你需要理解它并编辑它,而不是像你一样提交它;否则,你将导师会抓到你作弊。)

将单个值推送到表中的最后一个数组现在很容易实现:

size = (3 * ref->used) / 2;

这一次,你需要一个空表的特殊逻辑,既可以为数组分配描述,也可以为推送位置。

Pop更简单:

void table_push_value(table *ref, const item value)
{
    size_t  i;

    if (!ref) {
        fprintf(stderr, "table_push_value(): NULL table!\n");
        exit(EXIT_FAILURE);
    }

    /* Ensure there is at least one array. */
    if (!ref->size < 1) {
        /* Empty table: ref->size = 0, and ref->used must be 0 too. */
        const size_t  size = 1; /* Allocate for exactly one array. */
        void         *temp;

        temp = realloc(ref->array, size * sizeof ref->array[0]);
        if (!temp) {
            fprintf(stderr, "table_push_value(): Out of memory.\n");
            exit(EXIT_FAILURE);
        }

        ref->size = size;
        ref->array = temp;

        for (i = 0; i < size; i++)
            array_init(ref->array + i); /* array_init(&(ref->array[i])); */
    }

    if (ref->used > 0)
        i = ref->used - 1;  /* Last array in table */
    else
        i = 0; /* Table is empty, so use first array */

    array_push(ref->array + i, value); /* array_push(&(ref->array[i])); */
}

将整个数组推送到表(推送其中的项目数组,而不是复制项目)非常简单,但我们需要再次实施重新分配/增长策略:

item table_pop_value(table *ref)
{
    size_t  i;

    if (!ref) {
        fprintf(stderr, "table_pop_value(): NULL table!\n");
        exit(EXIT_FAILURE);
    }

    i = ref->used;

    /* Find the last array with items in it, and pop from it. */
    while (i-- > 0)
        if (ref->array[i].used > 0) {
            return array_pop(ref->array + i); /* array_pop(&(ref->array[i])); */

    fprintf(stderr, "table_pop_value(): Empty table, no items to pop!\n");
    exit(EXIT_FAILURE);
}

现在相应的pop操作应该非常明显:

void table_push_array(table *ref, array *one)
{
    if (!ref) {
        fprintf(stderr, "table_push_array(): NULL table!\n");
        exit(EXIT_FAILURE);
    }
    if (!one) {
        fprintf(stderr, "table_push_array(): NULL array!\n");
        exit(EXIT_FAILURE);
    }

    if (ref->used >= ref->size) {
        size_t  size, i;
        void   *temp;

        if (ref->used < 1)
            size = 1;  /* Minimum size is 1 */
        else
            size = (ref->used | 7) + 9; /* Next multiple of 8 */

        temp = realloc(ref->array, size * sizeof ref->array[0]);
        if (!temp) {
            fprintf(stderr, "table_push_array(): Out of memory.\n");
            exit(EXIT_FAILURE);
        }

        ref->array = temp;
        for (i = ref->size; i < size; i++)
            array_init(ref->array + i); /* array_init(&(ref->array[i])); */
        ref->size = size;
    }

    ref->array[ref->used] = *one; /* "shallow copy" */
    ref->used++;
}

上面array *table_pop_array(table *ref) { array retval = ARRAY_INIT; if (!ref) { fprintf(stderr, "table_pop_array(): NULL table!\n"); exit(EXIT_FAILURE); } if (ref->used < 1) { fprintf(stderr, "table_pop_array(): Table is empty, no arrays to pop!\n"); exit(EXIT_FAILURE); } /* Decrement the used count, so it refers to the last array in the table. */ ref->used--; /* Shallow copy the array. */ retval = ref->array[ref->used]; /* Init, but do not free, the array in the table. */ array_init(ref->array + ref->used); /* array_init(&(ref->array[ref->used)); */ /* Return the array. */ return retval; } 中有一个“技巧”,你应该明白。虽然我们不能返回指向局部变量的指针,但我们可以返回结构。在上面的例子中,结构描述了数组,其中的指针不是指局部变量,而是指动态分配的项目数组。结构类型可以像普通标量类型一样分配(如table_pop_array()int);它基本上与你分别指定每个成员一样。

总的来说,您应该注意到我没有使用过一次double电话。这是因为malloc()等同于realloc(NULL, size),只是将未使用的指针初始化为NULL会使一切变得更简单。

当表格增长(重新分配)时,由于上述malloc(size)使用模式,我们需要初始化所有新数组。

上述方法不排除直接访问表中的特定数组或数组中的特定项。如果您打算实现这样的函数,那么两个辅助函数类似于

realloc()

确保表格至少有void table_need_arrays(table *ref, const size_t size); void array_need_items(array *ref, const size_t size); 个数组的空间,并且数组至少有size个项目的空间。它们在连续推送多个项目或数组时也很有用,因为人们可以做到这一点。 size以确保表格中还有额外10个数组的空间。

在上述所有功能中,您都可以看到符号table_need_arrays(&mytable, mytable.used + 10);和带有相应name_of_array + index的评论。这是因为两个符号是等价的:指向&(name_of_array[index])index'元素的指针。

我没有费心去编译 - 检查上面的代码,因此可能会隐藏在其中的拼写错误。 (这也是故意的,因为我希望你理解代码,而不是仅仅复制它并在不了解任何细节的情况下将其用作自己的代码。)然而,逻辑是合理的。所以,如果你发现错字或问题,请在评论中告诉我,我会解决。