Błąd segmentation fault w liście dwukierunkowej

0
#include <iostream>

using namespace std;

template <typename T>
struct Node
{
	T data;
	Node *next;
	Node *prev;
};

template <typename T>
class DoublyLinkedList
{
public:
	Node<T> *head;
	Node<T> *prev;

	DoublyLinkedList()
	{
		head = NULL;
		prev = NULL;
	}

	void Insert(T newElement, int position)
	{
		Node<T> *newNode = new Node<T>();
		newNode->data = newElement;
		newNode->next = NULL;
		newNode->prev = NULL;

		if (position < 1)
		{
			cout << "\nposition should be >= 1.";
		}
		else if (position == 1)
		{

			newNode->next = head;
			head->prev = newNode;
			head = newNode;
		}
		else
		{

			Node<T> *temp = head;
			for (int i = 1; i < position - 1; i++)
			{
				if (temp != NULL)
				{
					temp = temp->next;
				}
			}

			if (temp != NULL)
			{
				newNode->next = temp->next;
				newNode->prev = temp;
				temp->next = newNode;
				if (newNode->next != NULL)
					newNode->next->prev = newNode;
			}
			else
			{
				cout << "\nThe previous node is null.";
			}
		}
	}

 int main()
{
	DoublyLinkedList<int> MyList;

	MyList.Insert(2312, 1);
	MyList.print();
 }

W linii head->prev = newNode; mam błąd Exception has occurred.Segmentation fault.

Wyjaśni mi ktoś co robię źle i przez co jest błąd? Z tego co sprawdzałem to wszystko wydaje się być ok i nie powinno być problemu z pamięcią, a tu taki zong.

4

i nie powinno być problemu z pamięcią

To jest problem z pamięcią. head jest nullptr w momencie dereferencji head->prev. Jak sobie postawisz breakpoint w tej linijce to debugger powinien Ci pokazać, że head nie pokazuje na nic (adres równy zero).

3

@Baxing Pytania na temat zadawaj poprzez normalne posty, komentarze są do offtopu.

Faktycznie, teraz to widzę. Tylko pytanie czym powinno być head jak nie nullem ?

head to wskaźnik pokazujący na pierwszy element listy. W internecie jest bardzo wiele darmowych zasobów w postaci tutoriali czy artykułów, które, jestem pewien, odpowiedzą Ci na wiele innych pytań, które możesz mieć w trakcie implementacji listy.

1

Ktos cię zmusił do tego fatalnego interfejsu, gdzie podajesz pozycję pod którą wstawić? Dużo lepsze by było połączenie funkcji dającej wskaźnik/iterator na n-ty element, oraz wstawianie po wybranym węźle. Dodatkowo, w DoublyLinkedList masz head oraz prev - ta druga nazwa ma mało sensu. Nie chcesz reprezentować czegoś "poprzedniego", tylko zapewne koniec listy. Zresztą widać że i tak tej zmiennej nie używasz.

3

Każda funkcja która ma więcej niż: 20 linii lub 3 instrukcje sterujące jest podejrzana.
Na dodatek, twój kod się nie kompiluje. Podaj Minimalny Kompletny Weryfikowalny Przykład.

https://godbolt.org/z/1nj5WGPhj

==1==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000010 (pc 0x00000040149d bp 0x7fff67602370 sp 0x7fff67602330 T0)
==1==The signal is caused by a WRITE memory access.
==1==Hint: address points to the zero page.
    #0 0x40149d in DoublyLinkedList<int>::Insert(int, int) /app/example.cpp:36
    #1 0x4012e3 in main /app/example.cpp:68
    #2 0x7fe0df3ed082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
    #3 0x40114d in _start (/app/output.s+0x40114d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /app/example.cpp:36 in DoublyLinkedList<int>::Insert(int, int)
==1==ABORTING

Czyli narzędzie wykrywa użycie nullptr (kodu nie analizowałem).
Ten błąd oznacza, że wykonując linię nr 36 to head->prev nie ma sensu bo head zawiera nullptr.

1

Wg mnie to powinno wyglądać mniej więcej następująco:

#include <iostream>
using namespace std;

template <typename T> class DoubleLinkedList // No thread safe!
{
	public:
	struct Node
	{
		T data;
		private:
		Node *next,*prev;
		Node(const T &data,Node *next=nullptr,Node *prev=nullptr):data(data),next(next),prev(prev) {}
		friend class DoubleLinkedList;
	};
	
	private:
	Node *head,*tail;
	
	public:
	DoubleLinkedList():head(nullptr),tail(nullptr) {}
	DoubleLinkedList(const DoubleLinkedList &lst)=delete; // lepiej jednak napisać
	DoubleLinkedList &operator=(const DoubleLinkedList &lst)=delete; // lepiej jednak napisać
	~DoubleLinkedList() { clear(); }
	
	bool isEmpty()const { return !head; }
	Node *front()const { return head; }
	Node *back()const { return tail; }
	Node *next(Node *from,int count)const { while((from)&&(count-->0)) from=from->next; return from; }
	Node *prev(Node *from,int count)const { while((from)&&(count-->0)) from=from->prev; return from; }
	void clear() { for(;(tail=head)!=nullptr;delete tail) head=head->next; }
	
	void insert(const T &data,int position)
	{
		if(position>=0) 
		{
			Node *node=next(head,position);
			if(node) insert_before(node,data);
			else insert_after(tail,data);
		}
		else // position=-1 => na koniec, position=-2 => przed ostatnim
		{
			Node *node=prev(tail,-position-1);
			if(node) insert_after(node,data);
			else insert_before(head,data);
		}
	}
	
	void insert_before(Node *node,const T &data)
	{
		if(!node) push_front(data);
		else node->prev=(node->prev?node->prev->next:head)=new Node(data,node,node->prev); // ?node->prev: => ?node->prev->next:
	}
	
	void insert_after(Node *node,const T &data)
	{
		if(!node) push_back(data);
		else node->next=(node->next?node->next->prev:tail)=new Node(data,node->next,node); // ?node->next: => ?node->next->prev:
	}
	
	void push_front(const T &data)
	{
		head=(head?head->prev:tail)=new Node(data,head,nullptr);
	}
	
	void push_back(const T &data)
	{
		tail=(tail?tail->next:head)=new Node(data,nullptr,tail);
	}
	
	ostream &prn(ostream &s)const
	{
		for(Node *node=head;node;node=node->next) s<<(node!=head?", ":"")<<node->data;
		return s;
	}
	
	ostream &nrp(ostream &s)const
	{
		for(Node *node=tail;node;node=node->prev) s<<(node!=tail?", ":"")<<node->data;
		return s;
	}
	
	friend ostream &operator<<(ostream &s,const DoubleLinkedList<T> &lst) { return lst.nrp(lst.prn(s)<<"] <=> ["); }	
};

int main()
{
	DoubleLinkedList<int> MyList;

	cout<<'['<<MyList<<']'<<endl;
	MyList.push_back(13);
	cout<<'['<<MyList<<']'<<endl;
	MyList.push_front(666);
	cout<<'['<<MyList<<']'<<endl;
	MyList.push_back(31);
	cout<<'['<<MyList<<']'<<endl;
	MyList.push_front(999);
	cout<<'['<<MyList<<']'<<endl;
	cout<<"****"<<endl;
	MyList.clear();
	MyList.insert(1,0);
	cout<<'['<<MyList<<']'<<endl;
	MyList.insert(2,1);
	cout<<'['<<MyList<<']'<<endl;
	MyList.insert(3,2);
	cout<<'['<<MyList<<']'<<endl;
	
	MyList.insert(6,-1);
	cout<<'['<<MyList<<']'<<endl;
	MyList.insert(5,-2);
	cout<<'['<<MyList<<']'<<endl;
	MyList.insert(4,-3);
	cout<<'['<<MyList<<']'<<endl;

	MyList.insert(0,-100);
	cout<<'['<<MyList<<']'<<endl;
	MyList.insert(7,100);
	cout<<'['<<MyList<<']'<<endl;
	
	return 0;
 }

@Baxing, zauważ że kodu o parę wierszy więcej niż u ciebie ale:

  • znacznie więcej możliwości - więc myśl na przód np poprawne usuwanie w destruktorze
  • wszystkie metody są jednowierszowe lub kilkuwierszowe - łatwo się szuka ewentualnego błędu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1