webentwicklung-frage-antwort-db.com.de

Was ist der Grund für die Verwendung eines Doppelzeigers beim Hinzufügen eines Knotens in eine verknüpfte Liste?

Die beiden folgenden Codebeispiele fügen beide einen Knoten oben in einer verknüpften Liste hinzu .. .. Während das erste Codebeispiel einen Doppelzeiger verwendet, verwendet das zweite Codebeispiel einen einzelnen Zeiger

Code Beispiel 1:

struct node* Push(struct node **head, int data)
{
        struct node* newnode = malloc(sizeof(struct node));
        newnode->data = data;
        newnode->next = *head;
        return newnode;
}

Push(&head,1);

Code Beispiel 2:

struct node* Push(struct node *head, int data)
{
        struct node* newnode = malloc(sizeof(struct node));
        newnode->data = data;
        newnode->next = head;
        return newnode;
}

Push(head,1)

Beide Strategien funktionieren. Viele Programme, die eine verknüpfte Liste verwenden, verwenden jedoch einen Doppelzeiger, um einen neuen Knoten hinzuzufügen. Ich weiß was ein Doppelzeiger ist. Aber wenn ein einzelner Zeiger ausreichen würde, um einen neuen Knoten hinzuzufügen, warum sind viele Implementierungen auf Doppelzeiger angewiesen?

Gibt es einen Fall, in dem ein einzelner Zeiger nicht funktioniert, so dass wir einen doppelten Zeiger benötigen?

40
a6h

Bei einigen Implementierungen wird ein Zeiger auf einen Zeigerparameter übergeben, damit der Kopfzeiger direkt geändert werden kann, anstatt den neuen Zeiger zurückzugeben. So könnten Sie schreiben:

// note that there's no return value: it's not needed
void Push(struct node** head, int data)
{
    struct node* newnode = malloc(sizeof(struct node));
    newnode->data=data;
    newnode->next=*head;
    *head = newnode; // *head stores the newnode in the head
}

// and call like this:
Push(&head,1);

Die Implementierung, die keinen Zeiger auf den Kopfzeiger benötigt, muss den neuen Kopf zurückgeben, und der Aufrufer ist für die Aktualisierung selbst verantwortlich:

struct node* Push(struct node* head, int data)
{
    struct node* newnode = malloc(sizeof(struct node));
    newnode->data=data;
    newnode->next=head;
    return newnode;
}

// note the assignment of the result to the head pointer
head = Push(head,1);

Wenn Sie diese Zuweisung beim Aufrufen dieser Funktion nicht ausführen, verlieren Sie die mit malloc zugewiesenen Knoten und der Kopfzeiger zeigt immer auf denselben Knoten.

Der Vorteil sollte jetzt klar sein: Wenn der Anrufer vergisst, den zurückgegebenen Knoten dem Kopfzeiger zuzuweisen, werden mit der zweiten Person schlimme Dinge passieren.

69

In Ihrem speziellen Beispiel ist der Doppelzeiger nicht erforderlich. Es kann jedoch erforderlich sein, wenn Sie beispielsweise Folgendes tun:

struct node* Push(struct node** head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=*head;
//vvvvvvvvvvvvvvvv
*head = newnode; //you say that now the new node is the head.
//^^^^^^^^^^^^^^^^
return newnode;
}
4
Armen Tsirunyan

Obwohl die vorherigen Antworten gut genug sind, denke ich, dass es viel einfacher ist, in Form von "Kopie nach Wert" zu denken. 

Wenn Sie einen Zeiger auf eine Funktion übergeben, wird der Adresswert in den Funktionsparameter kopiert. Aufgrund des Umfangs der Funktion wird diese Kopie nach der Rückkehr verschwinden. 

Mit einem Doppelzeiger können Sie den Wert des ursprünglichen Zeigers aktualisieren. Der Doppelzeiger wird weiterhin per Wert kopiert, aber das spielt keine Rolle. Alles, was Ihnen wirklich wichtig ist, ist das Ändern des ursprünglichen Zeigers, wodurch der Gültigkeitsbereich oder der Stack der Funktion umgangen wird.

Ich hoffe, dies beantwortet nicht nur Ihre Frage, sondern auch andere Fragen, die sich auf Zeiger beziehen.

2
user1164937

Nehmen wir das einfach an, zB:

void my_func(int *p) {
        // allocate space for an int
        int *z = (int *) malloc(sizeof(int));
        // assign a value
        *z = 99;

        printf("my_func - value of z: %d\n", *z);

        printf("my_func - value of p: %p\n", p);
        // change the value of the pointer p. Now it is not pointing to h anymore
        p = z;
        printf("my_func - make p point to z\n");
        printf("my_func - addr of z %p\n", &*z);
        printf("my_func - value of p %p\n", p);
        printf("my_func - value of what p points to: %d\n", *p);
        free(z);
}

int main(int argc, char *argv[])
{
        // our var
        int z = 10;

        int *h = &z;

        // print value of z
        printf("main - value of z: %d\n", z);
        // print address of val
        printf("main - addr of z: %p\n", &z);

        // print value of h.
        printf("main - value of h: %p\n", h);

        // print value of what h points to
        printf("main - value of what h points to: %d\n", *h);
        // change the value of var z by dereferencing h
        *h = 22;
        // print value of val
        printf("main - value of z: %d\n", z);
        // print value of what h points to
        printf("main - value of what h points to: %d\n", *h);


        my_func(h);

        // print value of what h points to
        printf("main - value of what h points to: %d\n", *h);

        // print value of h
        printf("main - value of h: %p\n", h);


        return 0;
}

Ausgabe:

main - value of z: 10
main - addr of z: 0x7ffccf75ca64
main - value of h: 0x7ffccf75ca64
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffccf75ca64
my_func - make p point to z
my_func - addr of z 0x1906420
my_func - value of p 0x1906420
my_func - value of what p points to: 99
main - value of what h points to: 22
main - value of h: 0x7ffccf75ca64

wir haben diese Signatur für my_func:

void my_func(int *p);

Wenn Sie die Ausgabe betrachten, ist der Wert, auf den h zeigt, immer noch 22, und der Wert von h ist derselbe, obwohl er in my_func geändert wurde. Woher ?

Nun, in my_func manipulieren wir den Wert von p, der nur ein lokaler Zeiger ist. Nach dem Aufruf:

my_func(ht);

in main () wird p den Wert halten, der h hält, der die Adresse der in der Hauptfunktion deklarierten z-Variablen darstellt.

Wenn wir in my_func () den Wert von p so ändern, dass er den Wert von z hält, der ein Zeiger auf eine Stelle im Speicher ist, für die wir Speicherplatz zugewiesen haben, ändern wir nicht den Wert von h, den wir haben übergeben, aber nur der Wert des lokalen Zeigers p. Grundsätzlich enthält p nicht mehr den Wert von h, sondern die Adresse eines Speicherplatzes, auf den z zeigt.

Nun, wenn wir unser Beispiel etwas ändern:

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

void my_func(int **p) {
    // allocate space for an int
    int *z = (int *) malloc(sizeof(int));
    // assign a value
    *z = 99;

    printf("my_func - value of z: %d\n", *z);

    printf("my_func - value of p: %p\n", p);
    printf("my_func - value of h: %p\n", *p);
    // change the value of the pointer p. Now it is not pointing to h anymore
    *p = z;
    printf("my_func - make p point to z\n");
    printf("my_func - addr of z %p\n", &*z);
    printf("my_func - value of p %p\n", p);
    printf("my_func - value of h %p\n", *p);
    printf("my_func - value of what p points to: %d\n", **p);
    // we are not deallocating, because we want to keep the value in that
    // memory location, in order for h to access it.
    /* free(z); */
}

int main(int argc, char *argv[])
{
    // our var
    int z = 10;

    int *h = &z;

    // print value of z
    printf("main - value of z: %d\n", z);
    // print address of val
    printf("main - addr of z: %p\n", &z);

    // print value of h.
    printf("main - value of h: %p\n", h);

    // print value of what h points to
    printf("main - value of what h points to: %d\n", *h);
    // change the value of var z by dereferencing h
    *h = 22;
    // print value of val
    printf("main - value of z: %d\n", z);
    // print value of what h points to
    printf("main - value of what h points to: %d\n", *h);


    my_func(&h);

    // print value of what h points to
    printf("main - value of what h points to: %d\n", *h);

    // print value of h
    printf("main - value of h: %p\n", h);
    free(h);


    return 0;
}

wir haben folgende Ausgabe:

main - value of z: 10
main - addr of z: 0x7ffcb94fb1cc
main - value of h: 0x7ffcb94fb1cc
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffcb94fb1c0
my_func - value of h: 0x7ffcb94fb1cc
my_func - make p point to z
my_func - addr of z 0xc3b420
my_func - value of p 0x7ffcb94fb1c0
my_func - value of h 0xc3b420
my_func - value of what p points to: 99
main - value of what h points to: 99
main - value of h: 0xc3b420

Nun haben wir tatsächlich den Wert geändert, den h von my_func hat, indem wir Folgendes tun:

  1. funktionssignatur geändert
  2. aufruf von main (): my_func (& h); Grundsätzlich übergeben wir die Adresse des Zeigers h an den Doppelzeiger p, der in der Funktionssignatur als Parameter deklariert ist.
  3. in my_func () machen wir: * p = z; dereferenzieren wir den Doppelzeiger p, eine Ebene. Im Grunde wurde dies so übersetzt, wie Sie es tun würden: h = z;

Der Wert von p enthält jetzt die Adresse des Zeigers h. Der Zeiger enthält die Adresse von z.

Sie können beide Beispiele nehmen und sie differenzieren. Um also auf Ihre Frage zurückzukommen, benötigen Sie einen Doppelzeiger, um Änderungen an dem Zeiger vorzunehmen, den Sie direkt von dieser Funktion übergeben haben.

1
bsd

Beobachten und Finden, WARUM ...

Ich habe mich entschlossen, ein paar Experimente durchzuführen und eine Schlussfolgerung zu ziehen,

BEOBACHTUNG 1 - Wenn die verknüpfte Liste nicht leer ist, können wir die Knoten (offensichtlich am Ende) nur mit einem einzigen Zeiger hinzufügen.

int insert(struct LinkedList *root, int item){
    struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
    temp->data=item;
    temp->next=NULL;
    struct LinkedList *p = root;
    while(p->next!=NULL){
        p=p->next;
    }
    p->next=temp;
    return 0;
}


int main(){
    int m;
    struct LinkedList *A=(struct LinkedList*)malloc(sizeof(struct LinkedList));
    //now we want to add one element to the list so that the list becomes non-empty
    A->data=5;
    A->next=NULL;
    cout<<"enter the element to be inserted\n"; cin>>m;
    insert(A,m);
    return 0;
}

Es ist einfach zu erklären (Basic). Wir haben einen Zeiger in unserer Hauptfunktion, der auf den ersten Knoten (Wurzel) der Liste zeigt. In der Funktion insert() übergeben wir die Adresse des Wurzelknotens und erreichen mit dieser Adresse das Ende der Liste und fügen einen Knoten hinzu. Wir können also daraus schließen, dass wir, wenn wir die Adresse einer Variablen in einer Funktion (nicht der Hauptfunktion) haben, den Wert dieser Variablen von dieser Funktion permanent ändern können, was sich in der Hauptfunktion widerspiegeln würde.

BEOBACHTUNG 2 - Die oben beschriebene Methode zum Hinzufügen eines Knotens schlug fehl, wenn die Liste leer war.

int insert(struct LinkedList *root, int item){
    struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
    temp->data=item;
    temp->next=NULL;
    struct LinkedList *p=root;   
    if(p==NULL){
        p=temp;
    }
    else{
      while(p->next!=NULL){
          p=p->next;
      }
      p->next=temp;
    }
    return 0;
}



int main(){
    int m;
    struct LinkedList *A=NULL; //initialise the list to be empty
    cout<<"enter the element to be inserted\n";
    cin>>m;
    insert(A,m);
    return 0;
}

Wenn Sie weiterhin Elemente hinzufügen und die Liste schließlich anzeigen, werden Sie feststellen, dass die Liste keine Änderungen erfahren hat und immer noch leer ist. Die Frage, die mir in den Sinn kam, war auch in diesem Fall, dass wir die Adresse des Wurzelknotens weitergeben, warum Änderungen nicht stattfinden, da permanente Änderungen und die Liste in der Hauptfunktion keine Änderungen erfahren. WARUM? WARUM? WARUM?

Dann habe ich eine Sache beobachtet, als ich A=NULL Geschrieben habe. Die Adresse von A wird 0. Dies bedeutet, dass A jetzt nicht auf eine Stelle im Speicher zeigt. Also habe ich die Zeile A=NULL; Entfernt und einige Änderungen an der Einfügefunktion vorgenommen.

einige Modifikationen (unter der Funktion insert() kann nur ein Element zu einer leeren Liste hinzugefügt werden. Diese Funktion wurde nur zu Testzwecken geschrieben.)

int insert(struct LinkedList *root, int item){
    root= (struct LinkedList *)malloc(sizeof(struct LinkedList));
    root->data=item;
    root->next=NULL;
    return 0;
}



int main(){
    int m;
    struct LinkedList *A;    
    cout<<"enter the element to be inserted\n";
    cin>>m;
    insert(A,m);
    return 0;
}

die obige Methode schlägt auch fehl, weil in der Funktion insert() root dieselbe Adresse wie A in der Funktion main() speichert, jedoch nach der Zeile root= (struct LinkedList *)malloc(sizeof(struct LinkedList)); die gespeicherte Adresse in root ändert sich. So speichern nun root (in insert() function) und A (in main() function) unterschiedliche Adressen.

Also das richtige Endprogramm wäre,

int insert(struct LinkedList *root, int item){
    root->data=item;
    root->next=NULL;
    return 0;
}



int main(){
    int m;
    struct LinkedList *A = (struct LinkedList *)malloc(sizeof(struct LinkedList));
    cout<<"enter the element to be inserted\n";
    cin>>m;
    insert(A,m);
    return 0;
}

Wir wollen jedoch nicht zwei verschiedene Funktionen zum Einfügen, eine, wenn die Liste leer ist, und eine, wenn die Liste nicht leer ist. Jetzt kommt der doppelte Zeiger, der die Sache einfacher macht.

Eine wichtige Sache, die mir aufgefallen ist, ist, dass Zeiger Adressen speichern und bei Verwendung mit '*' einen Wert für diese Adresse angeben, Zeiger jedoch selbst eine eigene Adresse haben.

Hier nun das komplette Programm und später die Konzepte erklären.

int insert(struct LinkedList **root,int item){
    if(*root==NULL){
        (*root)=(struct LinkedList *)malloc(sizeof(struct LinkedList));
        (*root)->data=item;
        (*root)->next=NULL;
    }
    else{
        struct LinkedList *temp=(struct LinkedList *)malloc(sizeof(struct LinkedList));
        temp->data=item;
        temp->next=NULL;
        struct LinkedList *p;
        p=*root;
        while(p->next!=NULL){
            p=p->next;
        }
        p->next=temp;
    }
    return 0;
}


int main(){
    int n,m;
    struct LinkedList *A=NULL;
    cout<<"enter the no of elements to be inserted\n";
    cin>>n;
    while(n--){
        cin>>m;
        insert(&A,m);
    }
    display(A);
    return 0;
}

Es folgen die Beobachtungen

1. root speichert die Adresse des Zeigers A (&A), *root Speichert die vom Zeiger A gespeicherte Adresse und **root Speichert den Wert an der von A gespeicherten Adresse. In einfacher Sprache root=&A, *root= A Und **root= *A.

2. Wenn wir *root= 1528 Schreiben, bedeutet dies, dass der Wert an der in root gespeicherten Adresse 1528 wird und da die in root gespeicherte Adresse die Adresse des Zeigers A ist (&A) Also jetzt A=1528 (Dh die in A gespeicherte Adresse ist 1528) und diese Änderung ist dauerhaft.

wenn wir den Wert von *root ändern, ändern wir tatsächlich den Wert an der in root gespeicherten Adresse, und seit root=&A (Adresse des Zeigers A) ändern wir indirekt den Wert von A oder in A gespeicherte Adresse.

wenn also A=NULL (Liste ist leer) *root=NULL, erstellen wir den ersten Knoten und speichern seine Adresse unter *root, dh wir speichern indirekt die Adresse des ersten Knotens unter A. Wenn die Liste nicht leer ist, ist alles dasselbe wie in den vorherigen Funktionen mit einem einzelnen Zeiger, außer dass wir root in *root Geändert haben, da das, was in root gespeichert war, jetzt in *root Gespeichert wird.

1
roottraveller

Wie @R. Martinho Fernandes in seiner Antwort hervorgehoben hat, können Sie mit Zeiger auf Zeiger als Argument in void Push(struct node** head, int data) den head-Zeiger direkt in Push ändern. Funktion statt den neuen Zeiger zurückzugeben.

Ein weiteres gutes Beispiel zeigt, warum die Verwendung von Zeiger auf Zeiger anstelle eines einzelnen Zeigers Ihren Code verkürzen, vereinfachen und beschleunigen kann. Sie fragten nach Hinzufügen eines neuen Knotens zur Liste, der im Gegensatz zu Entfernen des Knotens aus der einfach verknüpften Liste wahrscheinlich normalerweise keinen Zeiger-zu-Zeiger benötigt. Sie können das Entfernen eines Knotens aus der Liste ohne Zeiger auf Zeiger implementieren, dies ist jedoch suboptimal. Ich habe die Details hier beschrieben. Ich empfehle Ihnen, dieses YouTube-Video anzusehen, um das Problem zu beheben.

BTW: Wenn Sie mit Linus TorvaldsOpinion zählen, sollten Sie besser lernen, Zeiger-zu-Zeiger zu verwenden. ;-)

Linus Torvalds: (...) Am anderen Ende des Spektrums wünsche ich mir, dass mehr Leute die wirklich grundlegende Codierung auf niedriger Ebene verstehen würden. Nicht große, komplexe Dinge wie die lockless-Namensuche, aber einfach die Verwendung von Zeigern auf Zeigern usw. Zum Beispiel habe ich zu viele Leute gesehen, die einen einzeln verknüpften Listeneintrag löschen, indem sie den "prev" -Eintrag verfolgen und dann, um den Eintrag zu löschen, so etwas wie

if (prev)
prev->next = entry->next;
else
list_head = entry->next;

und wenn ich so Code sehe, gehe ich einfach "Diese Person versteht keine Zeiger". Und das ist leider ziemlich üblich.

Personen, die Zeiger verstehen, verwenden einfach einen "Zeiger auf den Eingabezeiger" und initialisieren diesen mit der Adresse des Listenkopfs. Wenn Sie die Liste durchlaufen, können Sie den Eintrag entfernen, ohne Bedingungen zu verwenden, indem Sie einfach "* pp = entry-> next" ausführen. (...)


Andere Ressourcen, die hilfreich sein können:

1
patryk.beza

Stellen Sie sich den Speicherort für den Kopf wie [HEAD_DATA] vor.

In Ihrem zweiten Szenario ist der main_head der aufrufenden Funktion der Zeiger auf diese Position.

main_head ---> [HEAD_DATA]

In Ihrem Code hat er den Wert des Zeigers main_head an die Funktion gesendet (d. H. Die Adresse des Speicherplatzes von head_data). .__ so jetzt

local_head ---> [HEAD_DATA]

und

main_head ---> [HEAD_DATA]

Beide zeigen auf den gleichen Ort, sind aber im Wesentlichen unabhängig voneinander. Wenn Sie also local_head = newnode schreiben; Was Sie getan haben, ist

local_head -/-> [HEAD_DATA]

local_head -----> [NEWNODE_DATA]

Sie haben einfach die Speicheradresse des vorherigen Speichers durch eine neue Adresse im lokalen Zeiger ersetzt. Der Hauptkopf (Zeiger) zeigt immer noch auf den alten [HEAD_DATA]

0
Napstablook

Wenn wir Zeiger als Parameter in einer Funktion übergeben und die Aktualisierung in demselben Zeiger wünschen, verwenden wir einen Doppelzeiger.

Wenn wir dagegen den Zeiger in einer Funktion als Parameter übergeben und ihn in einem einzelnen Zeiger abfangen, muss das Ergebnis an die aufrufende Funktion zurückgegeben werden, um das Ergebnis zu verwenden.

0
Kaushal Billore

Die Antwort liegt auf der Hand, wenn Sie sich die Zeit nehmen, eine Funktion zum Einfügen von Arbeitsknoten zu schreiben. deine ist keine.

Sie müssen in der Lage sein, write über den Kopf zu verschieben, um ihn vorwärts zu bewegen. Sie benötigen also einen Zeiger auf den Zeiger auf den Kopf, damit Sie ihn dereferenzieren können, um den Zeiger auf den Kopf zu setzen und ihn zu ändern.

0
Blindy

Nehmen wir an, ich habe Ihre Hausadresse auf einer Karte 1 notiert. Wenn ich nun Ihre Hausadresse mit jemandem teilen möchte, kann ich entweder die Adresse von Karte-1 nach Karte-2 kopieren und Karte-2 geben OR. Ich kann Karte-1 direkt geben. Auf beiden Wegen kann die Person die Adresse erfahren und Sie erreichen. Wenn ich jedoch card-1 direkt gebe, kann die Adresse auf card-1 geändert werden. Wenn ich card-2 angegeben habe, kann jedoch nur die Adresse auf card-2 geändert werden, nicht jedoch auf card-1.

Das Übergeben eines Zeigers an einen Zeiger ähnelt dem direkten Zugriff auf Karte 1. Das Übergeben eines Zeigers ähnelt dem Erstellen einer neuen Kopie der Adresse.

0
vishnu vardhan

Ich denke, der Punkt ist, dass es einfacher ist, Knoten innerhalb einer verknüpften Liste zu aktualisieren. Wo Sie normalerweise einen Zeiger für vorheriges und aktuelles verwenden müssen, können Sie einen Doppelzeiger haben, der sich um alles kümmert.

#include <iostream>
#include <math.h>

using namespace std;

class LL
{
    private:
        struct node 
        {
            int value;
            node* next;
            node(int v_) :value(v_), next(nullptr) {};
        };
        node* head;

    public:
        LL() 
        {
            head = nullptr;
        }
        void print() 
        {
            node* temp = head;
            while (temp) 
            {
                cout << temp->value << " ";
                temp = temp->next;
            }
        }
        void insert_sorted_order(int v_) 
        {
            if (!head)
                head = new node(v_);
            else
            {
                node* insert = new node(v_);
                node** temp = &head;
                while ((*temp) && insert->value > (*temp)->value)
                    temp = &(*temp)->next;
                insert->next = (*temp);
                (*temp) = insert;
            }
        }

        void remove(int v_)
        {
            node** temp = &head;
            while ((*temp)->value != v_)
                temp = &(*temp)->next;
            node* d = (*temp);
            (*temp) = (*temp)->next;
            delete d;
        }

        void insertRear(int v_)//single pointer
        {
            if (!head)
                head = new node(v_);
            else
            {
                node* temp = new node(v_);
                temp->next = head;
                head = temp;
            }
        }
};
0
user1044800

Ich denke, Ihre Verwirrung könnte von der Tatsache herrühren, dass beide Funktionen einen Parameter namens head haben. Die beiden head sind eigentlich verschiedene Dinge. head im ersten Code speichert die Adresse des Kopfknotenzeigers (der selbst eine Adresse der Kopfknotenstruktur speichert). Während das zweite head eine Adresse der Kopfknotenstruktur direkt speichert. Und da beide Funktionen den neu erstellten Knoten (der der neue Kopf sein sollte) zurückgeben, besteht meines Erachtens keine Notwendigkeit, den ersten Ansatz zu wählen. Anrufer dieser Funktion sind dafür verantwortlich, die Kopfreferenz, die sie haben, zu aktualisieren. Ich denke der zweite ist gut genug und einfach anzusehen. Ich würde mit dem zweiten gehen.

0
user3463521

Stellen Sie sich einen Fall vor, in dem Sie bestimmte Änderungen vornehmen müssen, und diese Änderungen sollten sich in der aufrufenden Funktion widerspiegeln.

Beispiel:

void swap(int* a,int* b){
  int tmp=*a;
  *a=*b;
  *b=tmp;
}

int main(void){
  int a=10,b=20;

  // To ascertain that changes made in swap reflect back here we pass the memory address
  // instead of the copy of the values

  swap(&a,&b);
}

In ähnlicher Weise übergeben wir die Speicheradresse des Kopfes der Liste.

Wenn auf diese Weise ein Knoten hinzugefügt und der Wert von Head geändert wird, ändert sich Reflects Back, und der Head in der aufrufenden Funktion muss nicht manuell zurückgesetzt werden.

Somit verringert dieser Ansatz die Wahrscheinlichkeit von Speicherverlusten, da wir den Zeiger auf den neu zugewiesenen Knoten verloren haben, wenn wir vergessen hätten, den Kopf in der aufrufenden Funktion zu aktualisieren.

Außerdem funktioniert der zweite Code schneller, da beim Kopieren und Zurückgeben keine Zeit verschwendet wird, da direkt mit dem Speicher gearbeitet wird.

0

Standardmäßig werden verknüpfte Listen in C behandelt, indem die Push- und Pop-Funktionen den Kopfzeiger automatisch aktualisieren. 

C ist "Call by Value", dh Kopien von Parametern werden an Funktionen übergeben. Wenn Sie nur den Kopfzeiger übergeben, werden alle lokalen Aktualisierungen, die Sie an diesem Zeiger vornehmen, vom Aufrufer nicht erkannt. Die zwei Problemumgehungen sind 

1) Übergeben Sie die Adresse des Kopfzeigers. (Zeiger auf Kopfzeiger)

2) Geben Sie einen neuen Kopfzeiger zurück und verlassen Sie sich auf den Aufrufer, um den Kopfzeiger zu aktualisieren. 

Option 1) ist die einfachste, wenn auch zunächst etwas verwirrend.

0
WilderField