如何动态添加项目到动态数组

时间:2017-08-10 03:31:28

标签: c pointers dynamic-arrays

我尝试使用函数将项添加到动态数组中。我正在做的是计算数组的大小并将其增加一。我的问题在于计算数组的大小; sizeof(*tab) / sizeof((*tab)[0])总是为零。

struct table
{
    char key[20];
    int val;
};

struct table *symTab, *insTab, *datTab;

void addFieldToTable(struct table **tab, char key[MAX_KEY_LENGTH], int val) {
    struct table temp;
    strcpy(temp.key, key);
    temp.val = val;

    size_t space;
    if (*tab == 0) {
        space = 1;
        *tab = malloc(space);
        *tab[0] = temp;
        return;
    }
    //#region for test only
    size_t sizeOfTab = sizeof(*tab);//4
    size_t sizeOfEntity1 = sizeof((*tab)[0]);//24
    size_t sizeOfEntity2 = sizeof(**tab);//24
    size_t sizeOfEntity3 = sizeof(struct table);//24
    //#endregion
    space = sizeof(*tab) / sizeof((*tab)[0]);//always gets zero
    space++;
    *tab = realloc(*tab, space);
    *tab[space - 1] = temp;

}

int main()
{
    addFieldToTable(&insTab, "mov", 0);
    addFieldToTable(&insTab, "cmp", 1);
}

在我的代码中,第一项成功添加,但在尝试添加第二项时,计算出的大小为零,因此数组不会调整大小,因此第一项*tab[0]将被替换通过新项目。

如何解决计算数组大小的问题,使函数能够动态添加项目?

3 个答案:

答案 0 :(得分:1)

你可能会使自己的事情变得比他们需要的更难。对于初学者,您可以通过在包含计数器的数据周围添加简单的包装来减少跟踪当前分配的table结构数所需的工作量。您也可以在此使用typedef

例如,不是直接处理table,而是为什么不将table的元素移动到它自己的简单结构中,比如tbl_data,只需在{{1}中包含一个指针数据和分配的table元素数量的计数器?它可以像这样工作:

tbl_data

接下来,避免使用全局变量。您已将typedef struct { char key[MAX_KEY_LENGTH]; int val; } tbl_data; typedef struct { /* simple wrapper preserving allocation size in ntbl */ tbl_data *tbl; size_t ntbl; } table; 作为参数传递,无需将**tab声明为全局。在insTab中将其声明为将其作为参数传递,因为它似乎是您要执行的操作。 (注意:使用包装器,您可以使用main()的简单静态实例并将其作为table传递)

考虑上面的结构方案。您只为每个添加的*tab分配,在分配验证新内存之后,您只需将tbl_data分配给{{1}并将temptab->tbl[tab->ntbl])的数量增加1.更简单。由于您每tbl_datatab->ntbl增加了分配,因此您可以使用1进行第一次和所有后续分配,从而简化addFieldToTable

注意:为每个添加的元素重复调用realloc(或addFieldToTablemalloc)不是一个非常有效的分配方案,但它很好现在。当realloc达到限制时,您可以分配calloc32512的块,或者每次只realloctbl->ntbl tbl->ntbl当您达到新限制以减少realloc来电时)

如果您在realloc中声明table insTab = { NULL, .ntbl = 0 };,那么您的main()验证可以简化为:{/ p>

addFieldToTable

注意:你应该真正将你的函数类型更改为void addFieldToTable (table *tab, char *key, int val) { if (!key) { /* validate key is not NULL */ fprintf (stderr, "error: key parameter is NULL\n"); return; } tbl_data temp; size_t keylen = strlen (key); /* get the length of key */ if (keylen > MAX_KEY_LENGTH) { /* truncate if > MAX_KEY_LENGTH */ key[keylen - 1] = 0; fprintf (stderr, "warning: key exceeds %d, truncated.\n", MAX_KEY_LENGTH); } strcpy (temp.key, key); temp.val = val; /* just realloc tbl_data, since you are increasing by 1 each time */ void *tmptbl = realloc (tab->tbl, (tab->ntbl + 1) * sizeof *(tab->tbl)); if (!tmptbl) { /* validate realloc succeeded or handle error */ fprintf (stderr, "error: memory exhausted at '%zu' elements.\n", tab->ntbl); return; } tab->tbl = tmptbl; /* assign new block to *tab */ tab->tbl[tab->ntbl] = temp; /* assign temp */ tab->ntbl++; /* increment ntbl */ } 并返回指向tbl_data*的指针,这样你就可以(1)返回成功/失败的指示,例如指针或tab->tbl;以及(2)指向新NULL的指针立即可用。您也可以将其声明为类型tbl_data并返回int以获得成功和1失败并保持上述(1)的好处)

完全放弃,你可以做类似以下的事情:

0

示例使用/输出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_KEY_LENGTH 20

typedef struct {
    char key[MAX_KEY_LENGTH];
    int val;
} tbl_data;

typedef struct {    /* simple wrapper preserving allocation size in ntbl */
    tbl_data *tbl;
    size_t ntbl;
} table;

void addFieldToTable (table *tab, char *key, int val) 
{
    if (!key) {     /* validate key is not NULL */
        fprintf (stderr, "error: key parameter is NULL\n");
        return;
    }

    tbl_data temp;
    size_t keylen = strlen (key);   /* get the length of key */

    if (keylen > MAX_KEY_LENGTH) {  /* truncate if > MAX_KEY_LENGTH */
        key[keylen - 1] = 0;
        fprintf (stderr, "warning: key exceeds %d, truncated.\n", 
                MAX_KEY_LENGTH);
    }

    strcpy (temp.key, key);
    temp.val = val;

    /* just realloc tbl_data, since you are increasing by 1 each time */
    void *tmptbl = realloc (tab->tbl, (tab->ntbl + 1) * sizeof *(tab->tbl));

    if (!tmptbl) {  /* validate realloc succeeded or handle error */
        fprintf (stderr, "error: memory exhausted at '%zu' elements.\n",
                tab->ntbl);
        return;
    }
    tab->tbl = tmptbl;              /* assign new block to *tab */

    tab->tbl[tab->ntbl] = temp;     /* assign temp    */
    tab->ntbl++;                    /* increment ntbl */
}

int main (void)
{
    table insTab = { NULL, .ntbl = 0 };

    addFieldToTable (&insTab, "mov", 0);
    addFieldToTable (&insTab, "cmp", 1);
    addFieldToTable (&insTab, "next command", 1234);

    if (insTab.ntbl == 0) {
        fprintf (stderr, "error: insTab is empty.\n");
        exit (EXIT_FAILURE);
    }

    for (size_t i = 0; i < insTab.ntbl; i++)
        printf ("%-*s : %d\n", MAX_KEY_LENGTH, 
                insTab.tbl[i].key, insTab.tbl[i].val);

    free (insTab.tbl);  /* don't forget to free allocated memory */

    return 0;
}

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

您必须使用内存错误检查程序,以确保您不会尝试在已分配的内存块的范围之外/之外进行写入,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了已分配的所有内存。

对于Linux $ ./bin/struct_table mov : 0 cmp : 1 next command : 1234 是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

valgrind

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有任何问题,请告诉我。

答案 1 :(得分:1)

这一行space = sizeof(*tab) / sizeof((*tab)[0]);//always gets zero确实很糟糕!

tab被声明为struct table **,因此*tab是指针而sizeof(*tab)只是指针的大小...假设{{1}包含多个单个元素,可能是struct table,因此整数除法通常为0。

所以你必须自己保留分配的大小(你没有直接的方法从C获得它)。但无论如何,sizeof(struct table) > sizeof(struct table *)可能是一项昂贵的操作,因此您应该分块并保留分配的大小和使用的大小。

答案 2 :(得分:0)

你正在为具有24B +填充的struct分配1B。

您需要做的是为/subroutinename\{(.*)$pattern(.*)\}/ sizeof分配内存。

struct

<强>的sizeof&#39; S

*tab = malloc(sizeof(struct table));

如果你想知道数组中有多少个元素,请传递另一个带有该信息的参数。

sizeof(*tab); // Is just size of pointer

sizeof(**tab); === sizeof(struct table);  // It's size of Structure

重新分配是一项昂贵的操作,您应该为100个结构分配空间,然后,如果需要更多空间,请重新分配200个结构。

如果您无法弄明白,那么您的代码就会更正

void addFieldToTable(struct table **tab, char key[MAX_KEY_LENGTH], int val, int noElements)

int noElements = 0;
addFieldToTable(&insTab, "mov", 0, noElements++);
addFieldToTable(&insTab, "cmp", 1, noElements++);
addFieldToTable(&insTab, "third", 1, noElements++);

<强>输出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_KEY_LENGTH 20
struct table
{
    char key[MAX_KEY_LENGTH];
    int val;
};

struct table *symTab, *insTab, *datTab;

void addFieldToTable(struct table **tab, char key[MAX_KEY_LENGTH], int val, int noElements) {
    struct table temp;
    strcpy_s(temp.key, key);
    temp.val = val;

    if (*tab == NULL)
    {
        *tab = malloc(sizeof(struct table));
        (*tab)[0] = temp;
        return;
    }

    *tab = realloc(*tab, ((noElements + 1) * sizeof(struct table)));

    (*tab)[noElements] = temp;
}

int main()
{
    int noElements = 0;
    addFieldToTable(&insTab, "mov", 0, noElements++);
    addFieldToTable(&insTab, "cmp", 1, noElements++);
    addFieldToTable(&insTab, "third", 1, noElements++);

    for (int i = 0; i < noElements; i++)
    {
        printf("\n%d: %s, %d", i+1, insTab[i].key, insTab[i].val);
    }

    return 0;
}