Утечка памяти в C (Valgrind)

#c #stack #valgrind

Вопрос:

Я реализую стек с минимумом. В этой программе я получаю сообщение об ошибке от valgrind. Что-то не так с функциями push() и main (). Когда я добавляю delete st; в функцию push (), я получаю еще больше ошибок. Я проверяю это через valgrind ./a.out. Извините за длинный код. Я также написал остальные функции для стека. Но в них нет ошибки, я оставил их в коде, где может быть ошибка.

 #include <cstring>
#include <iostream>

struct Stack {
  int data;
  int min;
  Stack* next;
};

void Push(Stack** top, int n) {
  Stack* st = new Stack();
  st->data = n;
  if (*top == NULL) {
    *top = st;
    (**top).min = n;
  } else {
    st->min = ((n <= (**top).min) ? n : (**top).min);
    st->next = *top;
    *top = st;
  }
  std::cout << "ok" << std::endl;
}

void Pop(Stack** top) {
  if (*top != NULL) {
    std::cout << (**top).data << std::endl;
    *top = (*top)->next;
  } else {
    std::cout << "error" << std::endl;
  }
}

int main() {
  Stack* top = nullptr;
  int m;
  std::cin >> m;
  std::string str;
  for (int i = 0; i < m;   i) {
    std::cin >> str;
    if (str == "push") {
      int value;
      std::cin >> value;
      Push(amp;top, value);
    }
    if (str == "pop") {
      Pop(amp;top);
    }
  }
  delete top;
}
 

Комментарии:

1. Почему Push функции-члены и не Pop являются функциями Stack ? Почему вы создали Stack класс, в котором половина функций, связанных со стеками, находится за пределами класса (плохая инкапсуляция)? Кроме того, способ избежать утечек памяти состоит в том, чтобы не писать код, который их создает. std::stack<int> это класс стека, который принимает целые числа и не имеет утечек памяти.

2. Я новичок в программировании на C . Раньше я писал на Python. Пожалуйста, не судите строго. Да, это может быть плохая инкапсуляция, но меня попросили написать это таким образом. Меня попросили реализовать все функции самостоятельно.

3. Переменная next — член Stack неинициализирована, когда *top == NULL . Идиоматический способ инициализации нового экземпляра объекта в C заключается в определении конструктора, таким образом, создание экземпляра и инициализация выполняются вместе.

4. Тогда вы в основном учитесь C , а не C . Попросите вернуть деньги. В наши дни и в эпоху C код не должен выглядеть так, как вам было поручено написать.

5. Есть new вход Push , так что, скорее всего, он должен быть delete Pop .

Ответ №1:

Когда вы просто delete top уничтожаете его (в вашем случае это ничего , но вы можете отвлечься на чтение о деструкторах, если вам интересно) и освободить выделенную динамическую память top . Однако на самом деле вы тоже хотите delete top->next top->next->next этого (если есть) и т. Д. Исправление:

 while (top) { // same as "while (top != nullptr) {"
    Stack* next = top->next; // we can't use `top` after we `delete` it, save `next` beforehand
    delete top;
    top = next;
}
 

Теперь о более общих вещах. Курс учит вас некоторым действительно старым C (почти просто C; хотя даже C здесь плохой). По крайней мере, все ваше целое Push() может быть заменено (благодаря ссылкам lvalue ( Typeamp; ) std::min и агрегированной инициализации) на:

 void push(Stack*amp; top, int n) {
    top = new Stack{n, std::min(n, top ? top->min : n), top};
    std::cout << "okn";
}
 

Я новичок в программировании на C . Раньше я писал на Python

Хорошая работа. К сожалению, такое обучение показывает, что C — это что-то слишком старое и ужасающее.

Редактировать

вот новое в Push, поэтому, скорее всего, в Pop должно быть удаление

Правильно (благодаря @molbdnilo). Вы должны delete pop удалять элементы, а не просто пропускать их.