Неопределенное поведение при удалении элемента из динамического массива структур - PullRequest
0 голосов
/ 06 января 2019

У меня есть массив структур n размера, динамически размещаемых, и каждая позиция массива также является массивом, с разными размерами для каждой позиции (массивом массивов).

Я создал функцию для удаления заданного массива [index], но я столкнулся с некоторым неопределенным поведением, например: Если массив имеет размер 3, если я удаляю массив [0], я не могу получить доступ к массиву [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*));

И каждый рельс: 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 голосов
/ 06 января 2019

При удалении элемента i выполните memmove все данные от i+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-массива? 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;

Количество mallocs останется неизменным, но думать о данных станет проще. И каждый указатель, который указывает на массив данных, имеет свою собственную переменную отслеживания размера. Распределение может выглядеть следующим образом:

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...