Wie ändere ich einen Zeiger, der in C an eine Funktion übergeben wurde?

Lesezeit: 8 Minuten

Benutzeravatar von Paul Wicks
Paul Wick

Ich habe also Code, etwa den folgenden, um eine Struktur zu einer Liste von Strukturen hinzuzufügen:

void barPush(BarList * list,Bar * bar)
{
    // if there is no move to add, then we are done
    if (bar == NULL) return;//EMPTY_LIST;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // and set list to be equal to the new head of the list
    list = newNode; // This line works, but list only changes inside of this function
}

Diese Strukturen sind wie folgt definiert:

typedef struct Bar
{
    // this isn't too important
} Bar;

#define EMPTY_LIST NULL

typedef struct BarList
{
    Bar * val;
    struct  BarList * nextBar;
} BarList;

und dann mache ich in einer anderen Datei so etwas wie das Folgende:

BarList * l;

l = EMPTY_LIST;
barPush(l,&b1); // b1 and b2 are just Bar's
barPush(l,&b2);

Danach zeigt l jedoch immer noch auf EMPTY_LIST, nicht auf die modifizierte Version, die innerhalb von barPush erstellt wurde. Muss ich list als Zeiger auf einen Zeiger übergeben, wenn ich ihn ändern möchte, oder ist eine andere dunkle Beschwörung erforderlich?

Benutzeravatar von geofftnz
geofftnz

Sie müssen einen Zeiger an einen Zeiger übergeben, wenn Sie dies tun möchten.

void barPush(BarList ** list,Bar * bar)
{
    if (list == NULL) return; // need to pass in the pointer to your pointer to your list.

    // if there is no move to add, then we are done
    if (bar == NULL) return;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = *list;

    // and set the contents of the pointer to the pointer to the head of the list 
    // (ie: the pointer the the head of the list) to the new node.
    *list = newNode; 
}

Dann verwenden Sie es wie folgt:

BarList * l;

l = EMPTY_LIST;
barPush(&l,&b1); // b1 and b2 are just Bar's
barPush(&l,&b2);

Jonathan Leffler schlug vor, den neuen Listenführer in den Kommentaren zurückzugeben:

BarList *barPush(BarList *list,Bar *bar)
{
    // if there is no move to add, then we are done - return unmodified list.
    if (bar == NULL) return list;  

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // return the new head of the list.
    return newNode; 
}

Verwendung wird:

BarList * l;

l = EMPTY_LIST;
l = barPush(l,&b1); // b1 and b2 are just Bar's
l = barPush(l,&b2);

  • Danke, ich hatte gedacht, dass dies das Problem war, aber gehofft, dass es nicht so war;)

    – Paul Wick

    20. April 2009 um 4:38 Uhr

  • Alternativ können Sie die Funktion den Zeiger auf den neuen Kopf der Liste zurückgeben lassen. BarList *barPush(BarList *list, Bar *bar)

    – Jonathan Leffler

    20. April 2009 um 4:40 Uhr

Allgemeine Antwort: Übergeben Sie einen Zeiger auf das, was Sie ändern möchten.

In diesem Fall wäre es ein Zeiger auf den Zeiger, den Sie ändern möchten.

Denken Sie daran, dass in C ALLES als Wert übergeben wird.

Sie übergeben einen Zeiger an einen Zeiger, so wie hier

int myFunction(int** param1, int** param2) {

// now I can change the ACTUAL pointer - kind of like passing a pointer by reference 

}

Ja, Sie müssen einen Zeiger an den Zeiger übergeben. C übergibt Argumente als Wert, nicht als Referenz.

Das ist ein klassisches Problem. Geben Sie entweder den zugewiesenen Knoten zurück oder verwenden Sie einen Zeiger auf Zeiger. In C sollten Sie einen Zeiger auf ein X an eine Funktion übergeben, an der Ihr X geändert werden soll. Da Sie möchten, dass ein Zeiger geändert wird, sollten Sie in diesem Fall einen Zeiger an einen Zeiger übergeben.

Benutzeravatar von Venkatesh-t.codes
Venkatesh-t.codes

Das Ändern eines Zeigers in einer anderen Funktion erfordert ein Konzept, das als mehrfache Indirektion bezeichnet wird. Ich werde es später erklären. Die Spoiler-Lösung angesichts von @geofftnz verwendet mehrere Indirektionen. Was ich versuche, ist, mein Bestes zu geben, um die mehrfache Indirektion in C zu erklären.

Betrachten Sie die folgenden zwei Programme, ich werde den Code durchgehen.

Das folgende Programm verwendet keine mehrfache Indirektion und schlägt daher fehl.

Programm mit Fehler:

// filename: noIndirection.c
#include <stdio.h>
#include <stdlib.h>

void allocater(int *ptrTempAllctr)
{
    ptrTempAllctr = malloc(sizeof(int));
    if (ptrTempAllctr == NULL) {
        perror("in allocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main() 
{
    int *ptrMain = NULL;
    allocater(ptrMain);
    if (ptrMain == NULL) {
        printf("ptrMain is points to NULL\n");
        return 1;
    }
    //free(ptrMain);  // we don't have to free because it will be invalid free.
    return 0;
}

Betrachten Sie das obige Programm (noIndirection.c), die eine Variable hat ptrMain es ist ein Zeiger, der auf ein int zeigt. Wenn es an eine Funktion übergeben wurde, wird im Funktionsbereich (Hauptteil) eine temporäre Zeigervariable erstellt, da Argumente der Funktion temporäre Variablen sind und gelöscht werden, wenn sie den Bereich verlassen.

Die temporäre Zeigervariable ptrTempAllctr (was ein Argument ist) zeigt auf was auch immer der Aufrufer (main) Variable der Funktion ptrMain(was darauf hinweist NULL) zeigte, als es als Argument an die Funktion übergeben wurde.

Wenn wir verwenden malloc() oder weisen Sie der temporären Variablen einen anderen Zeiger zu ptrTempAllctr dann zeigt es darauf, aber die Zeigervariable in caller (main)-Funktion, die als Argument an die to-Funktion übergeben wurde allocater() zeigt immer noch auf die gleichen Daten (also NULL), auf die vor dem Funktionsaufruf verwiesen wurde.

Wenn der Angerufene (allocater())-Funktion den Gültigkeitsbereich verlässt, wird die temporäre Zeigervariable aus dem Stapel entfernt und der Speicher bleibt nicht zugewiesen. Am Ende kommt es zu einem Speicherleck. Um diese Einschränkung zu umgehen, müssen wir mehrere Indirektionen verwenden.

MEHRERE INDIREKTION:

Multiple indirection when we use of pointer/s to pointer/s in varying level(with multiple `*`) eg: `int **pp, int ***ppp`, etc.

und wir weisen sie mit address-of(&) Operator.

Was Variablen vom Typ mehrfacher Indirektionszeiger tun, ist, dass wir einen Zeiger auf eine Zeigervariable selbst erstellen können, um das obige Programm zu reparieren. Dadurch können wir die Adresse der weitergeben ptrMain zu allocater()
mit diesem Aufruf

allocater(&ptrMain);

also obiges Programm noIndirection.c erlaubt uns dies nicht, sehen Sie sich das Programm an withIndirection.c um diese mehrfache Indirektion zu implementieren.

Wir brauchen einen Zeiger auf den int-Zeiger (int **ptrMain) als Funktionsargument für allocater() Funktion in diesem Fall, um das obige fehlerhafte Programm (noIndirection.c) zu lösen.

Dies wurde im folgenden Programm verwendet.

Das folgende Programm verwendet mehrfache Indirektion um den Fehler im vorherigen Programm zu beheben.

// filename: withIndirection.c
#include <stdio.h>
#include <stdlib.h>

void trueAllocater(int **ptrTrueAllocater)
{
    *ptrTrueAllocater = (int *) malloc(sizeof(int));
    if (ptrTrueAllocater == NULL) {
        perror("in trueAllocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main(void) 
{
    int *ptrMain = NULL;
    trueAllocater(&ptrMain);
    if (ptrMain == NULL) {
        printf("memory not allocated\n");
        return EXIT_FAILURE;
    }

    printf("memory allocated and assigned to ptrMain");
    printf(" from trueAllocater\n");

    free(ptrMain);
    return EXIT_SUCCESS;
}

siehe Programm withIndirection.c ab jetzt zum Nachschlagen.

Um unser Problem zu lösen, müssen wir die Adresse der Zeigervariablen übergeben ptrMain (trueAllocater(&ptrMain);) an den trueAllocater, um sich zu ändern ptrMain wohin es später zeigen muss trueAllocater() oder eine andere Funktion, um dies zu tun, muss die Funktion einen Indirektionszeiger mit der richtigen Indirektionsebene akzeptieren, was bedeutet, ein weiteres * hinzuzufügen, das der Argumentdeklaration nach meinem derzeitigen Verständnis für die übergebenen Variablen hinzugefügt wird.

Mittels müssen wir die haben trueAllocater() Funktionsargument als int ** aus int * in withIndirection.c im Gegensatz zu noIndirection.c
so wird die Indirektionsebene erfüllt.

Wenn die Adresse der Argumentvariable des Aufrufers ptrMainDie tatsächliche Adresse von wurde an die Funktion übergeben. der Zeitweilige ptrTrueAllocater Die Argumentvariable in der Funktion zeigt auf die Adresse der Zeigervariablen ptrMaindie Adresse von caller(main) Funktion nicht welche Zeigervariable ptrMain(welches ist NULL im Programm) zeigt auf in Funktion(main).

Dereferenzieren wir die ptrTrueAllocater Variable die Adresse, die ptrMain Punkte zu werden aufgedeckt, weil die ptrTrueAllocater temporäre Variable zeigt auf den Aufrufer (main) ptrMain Variable selbst, nicht ihr Inhalt.

Der Inhalt der dereferenzierten ptrTrueAllocater Variable ist die Adresse der Daten, auf die der Aufrufer gezeigt hat (main)s Variable (ptrMain), also müssen wir eine zusätzliche Dereferenzierung vornehmen, um die endgültigen Daten zu erhalten.

Wir müssen also einmal dereferenzieren, um die Adresse der zu erhalten ptrMain worauf es zeigt, um das Wo zu ändern ptrMain muss zweimal gezeigt und dereferenziert werden, um die tatsächlichen Daten zu erhalten, auf die durch die verwiesen wird ptrMain welches ist NULL.

@PaulWicks Sie wollten ändern, also müssen Sie einmal dereferenzieren, um zuzuweisen oder zu ändern, wohin es zeigt.

Die Absicht des mehrfachen Indirektierens mithilfe von Zeigern besteht darin, ein mehrdimensionales Array zu erstellen und Zeigerargumente zu übergeben, die auf etwas zeigen müssen.

Wir müssen die Variable entsprechend den Typen ändern, die wir wie folgt manipulieren müssen:

Jede Hinzufügung von * in der Deklaration erhöht die Zeigerindirektionsebene und jede Dereferenzierung verringert die Zeigerindirektionsebene, die sich den Daten nähert.

Wir können dieses Problem lösen, indem wir die Adresse an die aufrufende Funktion zurückgeben, die die erforderliche Zeigervariable zuweist.

Ja, wir können diese Multi-Indirektions-Variablensyntax zum Erstellen von ein- oder mehrdimensionalen Arrays verwenden. Dies wird Anfänger zunächst verwirren, wenn sie sich die Zeit nehmen, viel Code zu lesen, werden sie den Unterschied zwischen ihnen finden können.

Bitte korrigieren Sie mich, wenn ich falsch liege, geben Sie bitte Feedback und lassen Sie mich wissen, was die anderen Verwendungen von mehreren Indirektionszeigern sind. Entschuldigung für mein schlechtes Englisch. Dies sind die Ressourcen, die mir geholfen haben, mehrere Indirektionen zu verstehen.
https://boredzo.org/pointers/#function_pointers
https://cseweb.ucsd.edu/~ricko/rt_lt.rule.html

1415710cookie-checkWie ändere ich einen Zeiger, der in C an eine Funktion übergeben wurde?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy