C: невозможно получить указатель на массив структуры - PullRequest
1 голос
/ 14 апреля 2011

У меня есть следующие структуры и функции

// KEY
// ----------------------------
struct key  {
    double k1, k2;
};

// CELL
// ----------------------------
struct cell {
    double x, y, h, g, rhs;
    struct key *keys;
};

void cellPrintData(struct cell *c)  {
    printf("\n\tCELL\n\t.............\n");
    printf("\t%f\n", c->x);
    printf("\t%f\n", c->y);
    printf("\t%f\n", c->g);
    printf("\t%f\n", c->h);
    printf("\t%f\n", c->rhs);
    printf("\t%f\n", c->keys->k1);
    printf("\t%f\n", c->keys->k2);
}

/* cellCopyValues
 * ----------------------------
 * Copy values from source cell
 * into target cell.
 */
void cellCopyValues(struct cell *targetcell, struct cell *sourcecell)   {

    targetcell->x = sourcecell->x;  
    targetcell->y = sourcecell->y;  
    targetcell->h = sourcecell->h;  
    targetcell->g = sourcecell->g;  
    targetcell->rhs = sourcecell->rhs;  
    targetcell->keys->k1 = sourcecell->keys->k1;    
    targetcell->keys->k2 = sourcecell->keys->k2;    

}

/* cellDuplicate
 * ----------------------------
 * Create a duplicate cell using
 * values from given cell and return it.
 */
struct cell * cellDuplicate(struct cell *c) {


    struct cell *c2 = (struct cell *) malloc(sizeof(struct cell));
        if (c2 == NULL) {
        printf("--> Unable to malloc *c2!\n");
        errno = ENOMEM;
        return NULL;
        }
    c2->keys = (struct key *) malloc(sizeof(struct key));
        if (c2->keys == NULL) {
        printf("--> Unable to malloc *c2->keys!\n");
        errno = ENOMEM;
        return NULL;
        }
    cellCopyValues(c2, c);

    return c2;
}

Теперь я столкнулся с проблемой при получении массива struct из этого метода:

/* cellGetNeighbors()   
 * ----------------------------
 * Gets the neighbors of a cell
 */
struct cell * cellGetNeighbors(struct cell *c, struct cell *sstart, struct cell *sgoal, double km)  {

    int i;

    // CREATE 8 CELLS
    struct cell cn[8];

    //cellPrintData(c);

    for(i = 0; i < 8; i++)  {
        cn[i] = *cellDuplicate(c);
    }

    // MAKE THEM NEIGHBORS

    cn[0].y -= _DISTANCETOMOVE;
    cn[1].x -= _DISTANCETOMOVE;
    cn[2].y += _DISTANCETOMOVE;
    cn[3].x += _DISTANCETOMOVE;

    cn[4].x -= _DISTANCETOMOVE;
    cn[4].y -= _DISTANCETOMOVE;

    cn[5].x -= _DISTANCETOMOVE;
    cn[5].y += _DISTANCETOMOVE;

    cn[6].x += _DISTANCETOMOVE;
    cn[6].y += _DISTANCETOMOVE;

    cn[7].x += _DISTANCETOMOVE;
    cn[7].y -= _DISTANCETOMOVE;



    // CALCULATE g, h, rhs, key
    for(i = 0; i < 8; i++)  {
        cn[i].g = cellG(&cn[i], sgoal);
        cn[i].h = cellH(&cn[i], sstart);
        cn[i].rhs = _INFINITY;

        cn[i].keys = cellCalculateKey(&cn[i], km);
        //cellPrintData(&cn[i]);
    }

    // STORE THESE NEIGHBORS IN FILE.
    struct cell *cptr = &cn[0];
    cellPrintData(&cn[2]);
    return cptr;
}

.. в ЭТОМ методе-

struct cell * cellMinNeighbor(struct cell *c, struct cell *sstart, struct cell *sgoal, double km)   {

    // GET NEIGHBORS of c
    int i;
    struct cell *cn = cellGetNeighbors(c, sstart, sgoal, km);
    double sum[8];
    double minsum;
    int mincell;

cellPrintData(cn + 2);

    for(i = 0; i < 8; i++)  {
    //  sum[i] = 0.00;
    //  sum[i] += cellCost(c, cn + i);
    //  sum[i] += cellG(cn + i, sgoal);
    }
/*
    // Find min sum
    minsum = sum[0];
    mincell = 0;    
    for(i = 0; i < 8; i++)  {
        if(minsum < sum[i]) {
            minsum = sum[i];
            mincell = i;        
        }
    }

    //return (cn+mincell);
*/
    return cellCreateNew();
}   

Когда я сравниваю выходы cellPrintData () двумя способами -> method1: (отправитель)

CELL
.............
27.203030
71.435282
34.713147
0.000050
999.000000
34.713197
34.713147

method2: (получатель)

CELL
.............
27.203030
71.435282
34.713147
0.000050
999.000000
0.000000
0.000000

Это также приводит к очень большим значениям для k1 и k2 - и ошибкам сегментации.Что я делаю не так .. Спасибо ..:)

Ответы [ 2 ]

7 голосов
/ 14 апреля 2011

Ваша проблема здесь, в cellGetNeighbors:

struct cell cn[8];

Вы выделяете cn в стеке, поэтому, когда ваша функция cellGetNeighbors завершится и вернется, значение cn(версия cellGetNeighbors) больше не будет действительной, а cn в cellMinNeighbor будет указывать на часть стека, которая используется для чего-то другого.

У вас есть два простых варианта:

  1. Передайте массив из восьми struct cell в cellGetNeighbors, чтобы вызывающий отвечал за выделение этой памяти.
  2. Выделите cn в куче (то есть malloc) внутри cellGetNeighbors и верните его как есть.Вызывающая сторона, конечно, должна будет free возвращать cellGetNeighbors значение, когда оно будет завершено (и этот факт должен быть задокументирован как часть cellGetNeighbors интерфейса).

Я бы порекомендовал второй вариант, и я бы порекомендовал создать отдельную функцию cellFree, чтобы правильно освободить одну ячейку.Функция cellFree - хорошая идея, потому что в вашем struct cell есть указатель, и этот указатель необходимо освободить.Это, конечно, еще сложнее, если вам когда-либо понадобится использовать массив, который не имеет ровно восьми элементов;если это произойдет, вам придется также вернуть размер массива, добавив дополнительный аргумент-указатель к getCellNeighbors.Если дело доходит до этого момента, вам нужно добавить отдельную структуру:

struct cells {
    int n; /* How many cells there are */
    struct cell *items; /* The cells themselves */
}

и набор функций для выделения и освобождения этих новых структур.

Я предполагаю, чтоу вас есть аналогичные проблемы стека и кучи с cellCalculateKey.

И вам не нужно этого делать:

struct cell *cptr = &cn[0];
cellPrintData(&cn[2]);
return cptr;

Массив cn будет затухать до указателябез вашего вмешательства, это нормально:

cellPrintData(&cn[2]);
return cn;

Более того, поскольку я уже пишу здесь книгу, вам не нужно разыгрывать возврат из malloc (или calloc или realloc или что-либо еще, что возвращает void *) в C и это может скрыть проблемы.Итак, где вы говорите это:

struct cell *c2 = (struct cell *) malloc(sizeof(struct cell));
/* ... */
c2->keys = (struct keys *) malloc(sizeof(struct key));

вы должны просто сказать:

struct cell *c2 = malloc(sizeof(struct cell));
/* ... */
c2->keys = malloc(sizeof(struct key));

И еще одна вещь, у вас есть утечка памяти прямо здесь:

for(i = 0; i < 8; i++)  {
    cn[i] = *cellDuplicate(c);
}

Память, которую cellDuplicate выделяет для нового struct cell, просочилась.Вам было бы лучше с чем-то более похожим на это:

for(i = 0; i < 8; i++)  {
    cellDuplicateContent(c, &cn[i]);
}

И cellDuplicateContent будет просто копировать отдельные элементы и, конечно, выделять данные для keys, так как это указатель (то есть cellCopyValues плюс выделение keys).

1 голос
/ 14 апреля 2011

Похоже, что cellGetNeighbors возвращает стековую память - она ​​возвращает cptr, который указывает на & cn [0]. Как только метод возвращается, все, что вы объявили в стеке, больше не действует. Вы дублируете ячейки в этом, так что вам, вероятно, просто нужно распределить массив по размерам. Обратите внимание, что в конечном итоге вам также потребуется освободить этот массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...