Lista jednokierunkowa - warning, najlepsza implementacja?

0

W ramach nauki, piszę własną implementację listy jednokierunkowej, chciałbym się dowiedzieć co jest nie tak z poniższym kodem. Przy okazji zaznaczę, że zależy mi możliwie jak najprostszym/najlepszym rozwiązaniem.

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int val;
	struct node *next;
} node;

void insert(node**, int);
void insert_last(node**, int);
void printlist(node*);

// Main Function

int main()
{
	node *mylist = NULL;
	int i, n = 3;

	for (i = 0; i < n; i++)
		insert(&mylist, i);
	insert_last(&mylist, 5000);
	printf("My list:\n");
	printlist(mylist);
	free(mylist);
	return 0;
}

// Add a new element to list

void insert(node **head, int value)
{
	node *ptr = (node*)malloc(sizeof(node));
	ptr->val = value;
	ptr->next = *head;
	*head = ptr;
}

void insert_last(node **head, int value)
{
	if (*head == NULL)
	{
		insert(&head, value);
	}
	else
	{
		node *tmp = (node*)malloc(sizeof(node));
		tmp->val = value;
		tmp->next = NULL;

		node *ptr = *head; // do przechodzenia po liscie

		while (ptr->next != NULL)
		{
			ptr = ptr->next;
		}
		ptr->next = tmp;
	}
}

// Print all elements from list

void printlist(node *head)
{
	node *ptr = head;
	while (ptr != NULL)
	{
		printf("%d\n", ptr->val);
		ptr = ptr->next;
	}
}

Po skompilowaniu

$ gcc b.c -o b && ./b
b.c: In function ‘insert_last’:
b.c:48:10: warning: passing argument 1 of ‘insert’ from incompatible pointer type
   insert(&head, value);
          ^
b.c:34:6: note: expected ‘struct node **’ but argument is of type ‘struct node ***’
 void insert(node **head, int value)
      ^
My list:
2
1
0
5000

Aby dodać nowy element do listy, funkcja musi pobierać "głowę". Więc skąd to ostrzeżenie? Przecież node **head to wskaźnik na wskaźnik, który wskazuje na struct. Mógłby ktoś mi to wytłumaczyć?

2

Zły typ danych przekazujesz, kompilator dokładnie podpowiada.

insert(&head, value);

na insert(head, value);

 należy zmienić.
Dajesz adres wskaźnika na wskaźnik na node, a funkcja oczekuje wskaźnik na wskaźnik na node.
2
  1. Zapoznaj się z inkrementacją, bo jej nie rozumiesz: http://4programmers.net/Forum/1101404
  2. Użycie pętli przy wstawianiu elementu do listy niszczy wszystkie zalety listy.
  3. Jeżeli zależy ci na jak najprostszym rozwiązaniu to podziel to na dwie bądź trzy struktury: http://4programmers.net/Forum/C_i_C++/262765-listy_dodawanie_pierwszego_el?p=1203991#id1203991
1
node ** head;

head to wskaźnik na wskaźnik.
&head to wskaźnik na wskaźnik na wskaźnik.

0

free(mylist);
Dodajesz do listy, lecz jej nie czyścisz, przez co masz duży wyciek pamięci.
O ile w większości systemów dla Twojego przypadku nie ma to znaczenia, o tyle w przypadku gdy program będzie dłużej działał może to się źle skończyć.

0

Co sądzicie o tym?

#include <stdio.h>
#include <stdlib.h>
 
typedef struct node
{
    int val;
    struct node *next;
} node;
 
void insert(node**, int);
void insert_last(node**, int);
void printlist(node*);
void deletelist(node**);
 
// Main Function
 
int main()
{
    node *mylist = NULL;
    int i, n = 3;
 
    for (i = 0; i < n; ++i)
        insert(&mylist, i);

    insert_last(&mylist, 2000);

    printf("My list:\n");
    printlist(mylist);
    deletelist(&mylist);
    printf("\nDeleted list:\n");
    printlist(mylist);
    free(mylist);
    return 0;
}
 
// Add a new element to list
 
void insert(node **head, int value)
{
    node *ptr = (node*)malloc(sizeof(node));
    ptr->val = value;
    ptr->next = *head;
    *head = ptr;
}
 
void insert_last(node **head, int value)
{
    if (*head == NULL)
    {
        insert(head, value);
    }
    else
    {
        node *tmp = (node*)malloc(sizeof(node));
        tmp->val = value;
        tmp->next = NULL;
 
        node *ptr = *head;
 
        while (ptr->next != NULL)
        {
            ptr = ptr->next;
        }
        ptr->next = tmp;
    }
}
 
// Print all elements from list
 
void printlist(node *head)
{
    node *ptr = head;
    while (ptr != NULL)
    {
        printf("%d\n", ptr->val);
        ptr = ptr->next;
    }
}

// Delete list

void deletelist(node **head)
{
    while (*head != NULL)
    {
        node *ptr = *head;
        *head = ptr->next;
        free(ptr);
    }
}
_13th_Dragon napisał(a):

Użycie pętli przy wstawianiu elementu do listy niszczy wszystkie zalety listy.

Mógłbyś rozwinąć? :)

kq napisał(a):
node ** head;

head to wskaźnik na wskaźnik.
&head to wskaźnik na wskaźnik na wskaźnik.

Czyli &head jest adresem wskaźnika, który wskazuje na "głowę"?

Biały Van napisał(a):

free(mylist);
Dodajesz do listy, lecz jej nie czyścisz, przez co masz duży wyciek pamięci.
O ile w większości systemów dla Twojego przypadku nie ma to znaczenia, o tyle w przypadku gdy program będzie dłużej działał może to się źle skończyć.

Dodałem funkcję, która usuwa listę. Czy teraz program będzie poprawny? Jak mogę sprawdzić czy nie ma wycieku pamięci?

2

Nie wiem co tu rozwijać, koszt dodania do listy jest O(1) (pod linkiem masz przykład), ty masz O(n) czyli masz ten sam koszt co przydzielenie nowej tablicy i przekopiowanie, zaś nie masz dostępu poprzez indeks.
Podsumowując implementacja skopana.

0

Tylko wszędzie właśnie w taki sposób jest przedstawiona lista :( Ja się nie znam, bo dopiero co uczę...

1

Trzymaj sobie dwa wskaźniki - jeden na głowę, drugi na ogon; Przy dodawaniu na koniec listy używaj ogona, przy wstawianiu na początek - głowy; W ten sposób unikniesz iterowania po wszystkich węzłach, czyli złożoności O(n).

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