Проблема освобождения памяти из многомерного массива в C, free () вылетает программа - PullRequest
0 голосов
/ 02 февраля 2019

Я создаю deque для хранения строк в C, и когда я вызываю функцию free (), программа вылетает.Я реализовал похожую структуру, но только для хранения целых чисел, и не столкнулся с проблемами, но это, кажется, вызывает у меня некоторые.Я создал структуру, содержащую многомерный массив или символы, и я думаю, что я не правильно использую указатели?Я искал повсюду и не могу решить эту проблему Основная проблема, когда я вызываю clear () из тела ain.Это, в свою очередь, вызывает free (), и программа просто останавливается.:-( Любая помощь будет чрезвычайно полезна.

#include <stdio.h>

#define MAX 20 // number of characters for word

typedef struct {
  char **deque;
  int   size;
  int pFront;
  int pRear;
} deque;

typedef int bool;
enum { false, true };

void initDeque(deque *d, int initialSize) 
{
  d->size = initialSize;
  d->pFront = -1;
  d->pRear = -1;
  d->deque = (char **)malloc(sizeof(char*)*initialSize);
  int idx;
  for(int idx = 0; idx < d->size; idx++)
  {
    d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
    d->deque[idx] = "";
  } 
  printf("d->size: %zu\n", d->size);
}

void clear(deque *d) {
  if(d->pFront == -1)
  {
    printf("Queue is empty\n");
  }
  else
  {
    printf("Attempting to clear...\n");
    for(int idx = 0; idx < d->size; idx++)
    {
      printf("Attempting to clear columns...");
      free(d->deque[idx]);
    }
    printf("Attempting to clear rows...");
    free(d->deque);
    printf("Freed!!!!\n");
    d->deque = NULL;
    d->size = 0;
    d->pFront = -1;
    d->pRear = -1;
  } 
}


bool isEmpty(deque *d)
{
  if(d->pFront == -1){
    return true;
  }
  else
  {
    return false;
  }
}
bool isFull(deque *d)
{
  if(d->size == d->pRear+1)
  {
    return true;
  }
  else
  {
    return false;
  }
}

void display(deque *d)
{
  if(isEmpty(d)){
    printf("empty\n");
  }
  else{
    printf("Deque Values:\n");
    int idx;
    for(int idx = 0; idx <= d->pRear; idx++)
    {
      printf("Index: %zu\tValue: %s\n", idx, d->deque[idx]);
    } 
    printf("Size: %zu\n", d->size);
  } 
}
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{ 
  if(isFull(d))
  {
    printf("Is Full\n");
    int idx;
    deque dTemp;
    initDeque(&dTemp, d->size);
    printf("dTemp Initialised\n");
    for(idx = 0; idx < d->size; idx++)
    {
      dTemp.deque[idx] = d->deque[idx];
    }
    printf("deque copied to dTemp:\n");
    for(idx = 0; idx < d->size; idx++)
    {
      printf("dTemp[%zu]: %s\n", idx, dTemp.deque[idx]);
    }
    clear(&d);
    printf("d cleared\n");
    initDeque(&d, dTemp.size*2);
    printf("New deque of double length initialised\n");
    for(idx = 0; idx < dTemp.size; idx++)
    {
      d->deque[idx] = d->deque[idx];
    }
    printf("dTemp Copied to new deque\n");
    clear(&dTemp);
    printf("dTemp Cleared\n");
    char **tmp = realloc( d->deque, sizeof (d->deque) * (d->size*2) );
    if (tmp)
    {
        d->deque = tmp;
        for (int i = 0; i < d->size; i++)
        {   
            d->deque[d->size + i] = malloc( sizeof(char) * MAX );
        }
    }
  }
  printf("Appending to rear.. %s\n", item);
  d->pRear++;
  d->deque[d->pRear] = item;
  if(d->pFront == -1)
      d->pFront = 0;
}
int main(void)
{
    deque d;
    initDeque(&d, 5);
    rAppend(&d, "when");
    rAppend(&d, "will");
    rAppend(&d, "wendy");
    rAppend(&d, "walk");
    rAppend(&d, "with");
    display(&d);
    clear(&d);
  return 0;
}

Ответы [ 3 ]

0 голосов
/ 02 февраля 2019

Основная проблема заключается в том, что вы не понимаете разницу между копированием / назначением указателей и копированием / назначением данных, на которые они указывают .Во-вторых, кажется, вы можете не оценить полезность указателей, которые ни на что не указывают, особенно нулевые указатели.Далее следуют некоторые детали.


Вы динамически распределяете пространство для набора строк ...

  for(int idx = 0; idx < d->size; idx++)
  {
    d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));

... и затем пропускаете все этозаменив указатель на каждый указатель на пустой строковый литерал:

    d->deque[idx] = "";
  }

Как будто утечка не была достаточно плохой, вам не разрешено освобождать строковый литерал илиизмените его содержимое, что вы тем не менее пытаетесь сделать с любым из тех указателей, которые остаются в очереди, когда вы его clear().Вероятно, это является причиной некоторых ваших ошибок.

Если вы хотите установить каждую выделенную строку на пустую, то измените ее content вместо замены указателя на нее.Например:

    d->deque[idx][0] = '\0';

На самом деле, однако, вам, вероятно, не нужно даже этого делать.Вы уже ведете бухгалтерию, чтобы знать, какие массивы содержат действительные (строковые) данные, а какие нет, и этого должно быть достаточно для правильной работы.Предположим, вы в первую очередь храните копии строк.

Но это еще не все.Когда вы добавляете rAppend() элементов в вашу деку, у вас возникает похожая проблема.Вы создаете временную деку, а затем копируете строку указатели из оригинальной декы во временную:

dTemp.deque[idx] = d->deque[idx];

Это не только утечка оригинала (пусто)) данные во временной деке, она совмещает содержимое этой декы с основной декой.Когда вы позже очищаете временную деку, вы освобождаете все строковые указатели в оригинале.Последующее их использование или освобождение приводит к неопределенному поведению.

Возможно, вы вместо этого захотите strcpy() всех элементов основного deque в temp и обратно, но я предлагаю вместо этого пропустить временный deque вместе с чем-то в этом направлении:

void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{ 
  if(isFull(d))
  {
    printf("Is Full\n");
    char **tmp = realloc(d.deque, d->size * 2);

    if (tmp)
    {
        d->deque = tmp;
        for (int i = 0; i < d->size; i++)
        {   
            // Copied from the original, but see below
            d->deque[d->size + i] = malloc( sizeof(char) * MAX );
        }
        d->size * 2;
    }  // else?
  }
  printf("Appending to rear.. %s\n", item);
  d->pRear++;
  // Oops, this is another leak / aliasing issue:
  d->deque[d->pRear] = item;
  if(d->pFront == -1)
      d->pFront = 0;
}

Весь смысл временной deque для меня потерян, так как realloc(), который вам нужно сделать, сохраняет исходные данные в любом случае (до тех пор, пока это удается, в любом случае).

Обратите внимание, однако, что это все еще имеет проблему псевдонимов: вы связали элемент deque с добавленной строкой и вытекли из памяти, выделенной для этого элемента.Кроме того, когда вы очищаете deque, вы освобождаете эту строку для всех, кто держит указатель на нее.Или, по крайней мере, вы пытаетесь это сделать.Вам не разрешено делать это со строковыми литералами.

Я предлагаю вообще не выделять место в вашей деке для отдельных строк и не освобождать его.Продолжайте использовать назначение для хранения элементов в вашей deque, понимая и понимая, что это псевдонимы.Это будет более аналогично вашей реализации для int s.

0 голосов
/ 03 февраля 2019
#include<memory>
#include<iostream>
using namespace std;
struct S {
    S() { cout << "make an S\n"; }
    ~S() { cout << "destroy an S\n"; }
    S(const S&) { cout << "copy initialize an S\n"; }
    S& operator=(const S&) { cout << "copy assign an S\n"; }
};
S* f()
{
    return new S;   // who is responsible for deleting this S?
};
unique_ptr<S> g()
{
    return make_unique<S>();    // explicitly transfer responsibility for deleting this S
}
int main()
{
    cout << "start main\n";
    S* p = f();
    cout << "after f() before g()\n";
//  S* q = g(); // this error would be caught by the compiler
    unique_ptr<S> q = g();
    cout << "exit main\n";
    // leaks *p
    // implicitly deletes *q
}
0 голосов
/ 02 февраля 2019

Проблема в том, что вы вызываете free () в статической цепочке «когда», «будет», ...

Вы можете заменить вставку в функции void rAppend(deque *d, char item[]):

  d->deque[d->pRear] = item;

с:

  d->deque[d->pRear] = strdup(item);

Выполнение подобных цепочек распределяется в куче и освобождается от кучи.
После появления других проблем в коде, но он работает без сбоев.

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