#c #linked-list
#c #связанный список
Вопрос:
Я столкнулся с проблемой при кодировании реализации связанного списка на c . Всякий раз, когда я пытаюсь добавить элемент, я получаю ошибку ошибки сегментации.
#include <iostream>
class node{
public:
int data;
class node *next;
};
class LinkedList {
public:
node *head;
LinkedList(int num);
void add(int number);
void display();
};
const int null = 0;
LinkedList::LinkedList(int num) {
// TODO Auto-generated constructor stub
std::cout<<"Constructor Called"<<std::endl;
head->data = num;
head->next = null;
std::cout<<"Constructor Call completed"<<std::endl;
}
void LinkedList::add(int num) {
struct node *n=new node;
n->data = num;
n->next = null;
struct node *current = head;
while (current->next != null) {
current = current->next;
}
current->next = n;
}
void LinkedList::display() {
std::cout<<"Display"<<std::endl;
struct node *current = head;
while (current!= null) {
std::cout << current->data << "->";
current = current->next;
}
std::cout << "null"<<std::endl;
}
int main() {
LinkedList l(1);
l.display();
l.add(2);
l.display();
return 0;
}
Пожалуйста, загляните в журналы отладки gdb: узел n не ссылается ни на одну ячейку памяти. Поэтому его не удалось инициализировать. Пожалуйста, дайте мне знать, если вам потребуется дополнительная информация.
Заранее спасибо!
struct node *n=new node;
Вызывается конструктор Вызов конструктора завершен Точка останова 1, main () в SampleCPP.cpp: 60 60 л.display(); (gdb) s LinkedList::display (this=0xbffff03c) в SampleCPP.cpp:48 48 std::cout (gdb) n 51 while (current!= null) { (gdb) n 52 std:: данные cout "; (gdb) n 53 текущий = текущий-> следующий; (gdb) n 51 в то время как (текущий!= null) { (gdb) n 55 std::cout null 56 } (gdb) n main () в SampleCPP.cpp:61 61 l.add(2); (gdb) s LinkedList::add (this=0xbffff03c, num=2) в SampleCPP.cpp:38 38 узел структуры *n=новый узел; (gdb) вывести n $ 2 = (узел *) 0x0 (gdb)
Ответ №1:
Вы никогда не выделяете память для head
. Это ваша проблема.
Выделите его в конструкторе:
LinkedList::LinkedList(int num) {
std::cout<<"Constructor Called"<<std::endl;
head = new node; // This is missing!
head->data = num;
head->next = null;
std::cout<<"Constructor Call completed"<<std::endl;
}
и было бы неплохо освободить всю эту память до завершения работы вашей программы..
Комментарии:
1. Спасибо за вашу помощь. Это решает мою цель. Не могли бы вы сообщить мне, почему проблема не была выявлена в самом конструкторе? Я отладил конструктор, используя неправильный код, но сообщение о печати правильно напечатало данные в gdb.
2. Я не знаю этого наверняка, и это может потребовать дополнительного анализа, но я могу сформулировать гипотезу: если указатель не инициализируется, он может указывать на мусор, и запись в него может привести или не привести к сбою вашей программы (это называется неопределенным поведением , потому что вы не знаете, как это будет себя вести)
3. @Sambaran Я полагаю, вы имели в виду «почему проблема не была идентифицирована в самом конструкторе?» под «почему это не не идентифицировано во время компиляции. В противном случае утверждение бессмысленно. C не «применяет» какую-либо схему управления памятью, которую пользователь «должен» использовать, оставляя его решать, что делать, а что не делать. Если вы просто забудете выделить память для определенной переменной указателя (head здесь ), компилятор не заботится об этом (почему это должно быть?).
4. Да, Сарги прав. Я думал об этой интерпретации вашего вопроса, но я отказался от нее, поскольку она казалась слишком «очевидной», но я не даю ее как должное: отладчик не предназначен для проверки вашей программы на наличие неинициализированных переменных, неиспользуемого кода и тому подобного (это статический анализ), но он призван помочьвы обнаруживаете ошибку (например, предоставляя трассировку стека и переменные наблюдения после сбоя или в других сценариях)
Ответ №2:
вы не выделяете память для head; это должно быть так
LinkedList::LinkedList(int num) {
// TODO Auto-generated constructor stub
head=new node();
std::cout<<"Constructor Called"<<std::endl;
head->data = num;
head->next = null;
std::cout<<"Constructor Call completed"<<std::endl;
}
Комментарии:
1. При публикации ответов вы должны хотя бы сначала проверить, что он ничего не добавляет к моему..
Ответ №3:
С небольшой модификацией,
class node{
public:
int data;
class node *next;
node(int num): data(num), next(NULL){}
};
И
LinkedList::LinkedList(int num): head(new node(num)) {
// TODO Auto-generated constructor stub
std::cout<<"Constructor Called"<<std::endl;
//Do sth if you wish
std::cout<<"Constructor Call completed"<<std::endl;
}
Это устраняет некоторые дополнительные сложности. Измените, где когда-либо вы создавали экземпляр узла с помощью нового конструктора.
Ответ №4:
Я хотел бы предложить следующую реализацию:
#include <iostream>
using namespace std;
class node
{
private:
int data;
node* next;
public:
// Because of that we store the pointer, default implementations of the
// copying operations are not acceptable -- they can lead to memory leakage.
node(const nodeamp;) = delete;
nodeamp; operator =(const nodeamp;) = delete;
node(int d, node* prev = nullptr) :
data(d), next(nullptr)
{
if(prev)
{
// Free memory before assigning the pointer.
delete prev->next;
prev->next = this;
}
}
~node()
{
// Deletes up to the end of the subchain.
delete next;
}
inline node* getNext() const
{
return next;
}
inline operator int() const
{
return data;
}
};
class LinkedList
{
private:
node* head;
node* curr;
public:
LinkedList(int first_entry) :
head(new node(first_entry)), curr(head)
{}
~LinkedList()
{
delete head;
}
void add(int entry)
{
curr = new node(entry, curr);
}
void display() const
{
for(node* i = head; i; i = i->getNext())
cout << (int)*i << "->";
cout << "null" << endl;
}
};
int main(int argc, char* argv[])
{
LinkedList l(1);
l.display();
l.add(2);
l.display();
return EXIT_SUCCESS;
}
В этом подходе дополнительное внимание было уделено управлению памятью. Кроме того, он более интенсивно использует ООП.