从动态结构数组中删除元素时的未定义行为

时间:2019-01-06 19:14:27

标签: c arrays struct dynamic-memory-allocation

我有一个动态分配的n大小的结构数组,该数组的每个位置也是一个数组,每个位置(数组的数组)的大小不同。

我创建了一个删除给定数组[索引]的函数,但是我面临一些未定义的行为,例如: 如果数组的大小为3,则删除array [0]时将无法访问array [1]。其他索引组合也会发生这种情况。完美无瑕的唯一方法是从头到尾删除。

这是我的代码: 结构:

typedef struct point{

    char id[5];
    char type[5];
    char color[10];
    int x;
    int y;
} Point;


typedef struct {

    char lineID[5];
    int nPoints;
    Point *pt;
}railData;

typedef struct railway {
    railData data;
}railway;

这是创建数组的方式:

headRail = (railway**)calloc(lineNum,sizeof(railway*));

以及每个Rail:     headRail [i] =(railway *)calloc(pointsNum,sizeof(railway));

这些是删除滑轨的功能:

railway **delRail(railway **headRail, int j)
{
    int nPts = 0;

    if (!headRail)
    {
        puts(ERRORS[NULLPOINTER]);
        return NULL;
    }

    // Number of rail points on jth rail

    nPts = headRail[j]->data.nPoints;

    // Free each rail point from jth rail
    for (int i = 0; i < nPts; ++i)
    {
        free(headRail[j][i].data.pt);
    }

    // Free allocated memory for jth rail 
    free(headRail[j]);

    return headRail;
}

这是我调用上一个函数的地方:

railway **removeRail(railway **headRail)
{
    char userID[20];
    int index = 0;

    // Quit if no rails 
    if (!headRail)
    {
        backToMenu("No rails available!");
        return NULL;
    }

    // Get user input
    getString("\nRail ID: ",userID,MINLEN,MAXLEN); // MINLEN = 2 MAXLEN = 4



    // get index of the asked rail
    getRailIndex(headRail,userID,&index);

    if (index != NOTFOUND)
    {
        headRail = delRail(headRail, index);
        // Update number of rails in the array (global var)
        NUMOFRAILS--;

        backToMenu("Rail deleted!\n");
    }
    else
        backToMenu("Rail not found!");

    return headRail;
}

所以我的问题是我该如何修改我的代码,以便在消除位置i时,将所有其他索引向左移,最后一个空的位置将被丢弃(类似于realloc,但用于缩小)

我要的是在不更改数组结构的情况下可行吗?

1 个答案:

答案 0 :(得分:1)

删除元素i时,将memmovei+1的所有数据i到数组的末尾,然后realloc的大小减小通过1。

请注意,C语言中的数组不会以任何方式跟踪其大小,因此您需要通过外部方式传递大小。

您的数据抽象很奇怪。我希望使用headRail[j][0].data.nPoints来存储headRail[j][0].data结构内部的点数,但是在那里您将headRails的计数存储在j行headRail[j][<this count>]中。我建议重写一下抽象,为铁路提供一个“对象”,为整个方向上动态尺寸的二维铁路阵列添加一个“对象”。

赞:

railway **delRail(railway **headRail, int j)
{
    ...

    // this is strange, it's equal to
    // nPts = headRail[j][0].data.nPoints;
    // dunno if you mean that, 
    // or if [j][0].data.nPoints refers to the size of 
    // headRail[j][0].data.pt or to the size of the whole array
    size_t nPts = headRail[j]->data.nPoints;
    for (size_t i = 0; i < nPts; ++i) {
        free(headRail[j][i].data.pt);
    }
    free(headRail[j]);

    // note that arrays in C does not know how many elements are there in the array
    // so you typically pass that along the arguments, like
    // railway **delRail(railway **headRail, size_t railcount, int j);
    size_t headRailCount = lineNum; // some external knowledge of the size
    memmove(&headRail[j], &headRail[j + 1], (headRailCount - j - 1) * sizeof(*headRail));
    void *pnt = realloc(headRail, (headRailCount - 1) * sizeof(*headRail));
    if (pnt == NULL) return NULL; // that would be strange
    headRail = pnt; // note that the previous headRail is no longer valid
    --lineNum; // decrement that object where you store the size of the array

    return headRail;
}

一些封装和更多的结构而不是二维数组呢? 2d数组确实让C感到有些痛苦,那呢:

typedef struct {
    // stores a single row of rail datas
    struct railData_row_s {
        // stores a pointer to an array of rail datas
        railData *data;
        // stores the count of how many datas of rails are stored here
        size_t datacnt;
    // stores a pointer to an array of rows of rail datas
    } *raildatas;
    // stores the size of the pointer of rows of rail datas
    size_t raildatascnt;
} railway;

malloc的数量将保持不变,但是考虑数据将变得更加简单。每个指向数据数组的指针都有自己的大小跟踪变量。分配可能看起来像这样:

railway *rail_new(size_t lineNum, size_t pointsNum) {
   railway *r = calloc(1, sizeof(*r));
   if (!r) { return NULL; }

   // allocate the memory for rows of raildata
   r->raildatascnt = lineNum;
   r->raildatas = calloc(r->raildatascnt, sizeof(*r->raildatas));
   if (!t->raildatas) { /* error hadnling */ free(r); abort(); }

   // for each row of raildata
   for (size_t i = 0; i < r->raildatascnt; ++i) {
        struct railData_row_s * const row = &r->raildatas[i];

        // allocate the memory for the column of raildata
        // hah, looks similar to the above?
        row->datacnt = pointsNum;
        row->data = calloc(row->datacnt, sizeof(*row->data));
        if (!row->data) { /* error ahdnling */ abort(); }

   }

   return r;
}