74 Stimmen

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

Ich habe also einen Code, etwa wie 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 in einer anderen Datei mache ich 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 und nicht auf die geänderte Version, die innerhalb von barPush erstellt wurde. Muss ich list als Zeiger auf einen Zeiger übergeben, wenn ich sie ändern will, oder ist eine andere dunkle Beschwörung erforderlich?

3voto

Das Ändern eines Zeigers in einer anderen Funktion erfordert ein Konzept namens multiple Indirektion, ich werde es später erklären, die Spoilerlösung von @geofftnz verwendet multiple Indirektion. Ich versuche mein Bestes, um die mehrfache Indirektion in C zu erklären.

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

Das folgende Programm verwendet keine Mehrfachumleitung 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 einen int zeigt. Wenn er an eine Funktion übergeben wurde, wird im Funktionsbereich (body) eine temporäre Zeigervariable erstellt, da Argumente der Funktion temporäre Variablen sind, die gelöscht werden, wenn sie den Funktionsbereich verlassen.

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

Wenn wir die malloc() oder einen anderen Zeiger auf die temporäre Variable zuweisen ptrTempAllctr dann zeigt er darauf, aber die Zeigervariable in caller( main ) Funktion, die als Argument an die to-Funktion übergeben wurde allocater() immer noch auf die gleichen Daten verweist (die NULL ), auf die er vor dem Funktionsaufruf gezeigt hat.

Wenn der aufgerufene ( allocater() ) wird die temporäre Zeigervariable aus dem Stack entfernt und der nicht zugewiesene Speicher wird durch ein Speicherleck ersetzt. Um diese Einschränkung zu umgehen, müssen wir mehrere Umleitungen verwenden.

MEHRFACHE UMLEITUNG:

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( & ) Betreiber.

Die Variablen des Typs "Multiple Indirection Pointer" ermöglichen es uns, eine einen Zeiger auf eine Zeigervariable selbst zu machen, um das obige Programm zu fixieren. Dies erlaubt uns, die Adresse der ptrMain zu allocater() mit diesem Aufruf

allocater(&ptrMain);

also das obige Programm noIndirection.c dies nicht zulässt, siehe das Programm withIndirection.c um diese mehrfache Umleitung zu implementieren.

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

Dies wurde in dem folgenden Programm verwendet.

Das folgende Programm verwendet Mehrfachumgehung 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 das Programm withIndirection.c als Referenz von nun an.

Um unser Problem zu lösen, müssen wir die Adresse der Zeigervariablen übergeben ptrMain ( trueAllocater(&ptrMain); ) an den trueAllocater, um die ptrMain wohin sie später in den folgenden Abschnitten zeigen muss trueAllocater() oder eine andere Funktion, Dazu muss die Funktion einen Umleitungszeiger mit der richtigen Umleitungsebene akzeptieren, was bedeutet, dass der Argumentdeklaration für die übergebenen Variablen ein weiterer * hinzugefügt werden muss, um mein derzeitiges Verständnis zu verbessern.

Dazu benötigen wir die trueAllocater() Funktionsargument als int ** von int * in withIndirection.c im Gegensatz zu noIndirection.c damit die Umleitungsebene statistisch erfasst wird.

Wenn die Adresse der Argumentvariablen des Aufrufers ptrMain Adresse an die Funktion übergeben wurde. die temporäre ptrTrueAllocater Argumentvariable in Funktion zeigt auf die Adresse der Zeigervariablen ptrMain Adresse in caller( main ) Funktion nicht welche Zeigervariable ptrMain (das ist NULL im Programm) zeigt auf in function( main ).

Wenn wir die Dereferenzierung der ptrTrueAllocater Variable die Adresse, die ptrMain wird offenbart werden, weil die ptrTrueAllocater temporäre Variable zeigt auf den Aufrufer( main ) ptrMain Variable selbst, nicht ihren Inhalt.

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

also müssen wir einmal dereferenzieren, um die Adresse der ptrMain auf die er zeigt, um den Ort zu ändern ptrMain muss gezeigt und dereferenziert werden zweimal, um die tatsächlichen Daten zu erhalten, auf die die ptrMain das ist NULL .

@PaulWicks Sie beabsichtigt, zu ändern, so dass Sie zu dereferenzieren einmal zuzuweisen oder zu ändern, wo seine zeigen.

Die Absicht der mehrfachen Umleitung mit Zeigern ist es, mehrdimensionale Arrays zu erstellen und Zeigerargumente zu übergeben, die auf etwas gezeigt werden müssen.

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

jede Hinzufügung von * in der Deklaration erhöht den Grad der Zeigerumlenkung und jede Dereferenzierung verringert die Zeiger-Indirektionsebene, d.h. sie führt zu einer Annäherung an die Daten.

Wir können dieses Problem lösen, indem wir die Adresse an die aufrufende Funktion zurückgeben und der erforderlichen Zeigervariablen zuweisen.

Ja, wir können diese Multi-Indirektions-Variablen-Syntax zur Erstellung von ein- oder mehrdimensionalen Arrays verwenden. Dies wird Anfänger zunächst verwirren, aber wenn sie sich Zeit nehmen, um Zeit investieren, um viel Code zu lesen, werden sie in der Lage sein, den Unterschied zwischen ihnen zu erkennen.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X