Sonntag, Dezember 8, 2019

Verwirrt durch die einzelnen Zeiger und Doppel-pointer-Argumente in Funktionsaufrufen

Ich versuche, ein tieferes Verständnis über pointer-Argumente in Funktionen für C. ich geschrieben habe ein test-Programm, um zu versuchen, um den Unterschied zu sehen zwischen der Weitergabe einer einzigen Zeiger vs double Zeiger auf eine Funktion, und dann zu modifizieren.

Ich habe ein Programm, das zwei Funktionen hat. Die erste Funktion modifyMe1 mit einem einzigen Zeiger als argument und ändert eine Eigenschaft zum 7. Die zweite Funktion modifyMe2 nimmt einen double-pointer als argument und ändert eine Eigenschaft zum 7.

Ich erwartet, dass die erste Funktion modifyMe1 wäre: „pass-by-value“, das ist, wenn ich ging in mein struct-pointer, C schaffen würde, eine Kopie der Daten zeigte es. Während bei den letzteren, ich bin dabei ein „pass-by-reference“, das sollte ändern Sie die Struktur an Ort und Stelle.

Jedoch, wenn ich testen Sie dieses Programm aus, beide Funktionen scheinen zu ändern, die Struktur im Ort. Ich weiß, es ist ein Missverständnis für mich auf die Art der Zeiger-Argumente für sicher. Kann mir jemand helfen, deaktivieren Sie dies für mich?

Dank!

Hier ist was ich habe:

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

struct myStructure {
    int a;
    int b;
};

void modifyMe1(struct myStructure *param1) {
    param1->a = 7;
}

void modifyMe2(struct myStructure **param1) {
    (*param1)->a = 7;
}

int main(int argc, char *argv[]) {
    struct myStructure *test1;

    test1 = malloc(sizeof(test1));
    test1->a = 5;
    test1->b = 6;

    modifyMe1(test1);

    printf("a: %d, b: %d\n", test1->a, test1->b);

    //set it back to 5
    test1->a = 5;
    printf("reset. a: %d, b: %d\n", test1->a, test1->b);

    modifyMe2(&test1);

    printf("a: %d, b: %d\n", test1->a, test1->b);


    free(test1);
    return 0;
}

In dem meine Ausgabe ist:

$ ./a
a: 7, b: 6
reset. a: 5, b: 6
a: 7, b: 6
  • Sie können immer ändern, was ein Zeiger verweist. Jedoch, wie alle Dinge in C als Wert übergeben werden (Kopie), Sie können nicht ändern, die Funktion-argument, so dass der Anrufer sehen kann das ändern, es sei denn, Sie fügen Sie eine Ebene der Dereferenzierung. d.h., dieser code ändert nur die lokale Kopie; void foo(int *p) { p = malloc(size); }.
  • wenn Zeiger verwenden, dann übergeben, indem Zeiger, nicht nach Wert. Wenn die doppelten Zeiger, übergeben Sie die Adresse des Zeigers. Wenn Sie wollen, um pass “ – Wert übergeben, ohne jegliche Hinweise.
  • Ich meine nicht zu pedantisch sein, aber diese Frage scheint zu verwirren, alle Anfänger, alles, was in C durch Wert übergeben wird. Enthält Zeiger als gut.
  • Ja, das ist verwirrend, denn wenn Sie an Wert von etwas, das nicht ist ein Zeiger heißt es pass-by-value, und geben Sie den Wert des Zeigers, dies wird als pass-by-Zeiger.
  • Ich habe noch nie den Begriff „pass-by-pointer“. Ich denke, es ist mehr einfach nur verstehen, dass, in C, alles von Wert übergeben wird. Zeiger sind nichts besonderes in keiner Weise.
  • Hi Ed, werden Sie sagen, dass mit einer Doppel-Zeiger als argument erlaubt es uns, um die Adresse zu ändern, was er momentan zeigt? Ich denke, ich bin nicht klar, was der „Umweg“ bedeutet… :-\
  • Ich habe festgestellt, dass dies Begriffe, die in verschiedenen Büchern. Ich denke, dass es in jeder Sprache, wo-stack verwendet wird alles, was von Wert übergeben wird. Zum Beispiel, in Java gibt es pass-by-value und pass-by-reference beim kopieren der Wert oder die Adresse auf den stack geschoben wird, und diese Terminologie ist weit verbreitet…
  • Es erlaubt Ihnen, zu ändern, was die Zeiger pooints, ja. In diesem Fall verweist es auf die ein Zeiger zeigt, so dass Sie ändern können, ist es Wert (also eine Adresse) und der Anrufer sehen die änderung, weil Sie geändert werden, dass der Speicher direkt.
  • Ich bin mir nicht sicher, was es zu tun hat mit dem stack, die eine Implementierung detail, wie die einheimischen gespeichert sind. Die Skillung gar nicht erwähnen, einem „stack“, nur variable Lebensdauer. Java hat auch einen stack. C# hat einen Stapel. Das hat nichts damit zu tun, mit dem argument übergeben Semantik
  • javadude.com/articles/passbyvalue.htm

3 Kommentare

  1. 19

    Können Sie pass-argument in unterschiedlicher Weise in C (Captain Obvious, ja).

    1. Wert. Dann ist es kopiert zu stapeln. Also die Funktion hat lokale Kopie der variable in der Funktion frame. Änderungen argument nicht ändern übergebenen Wert. Es ist wie „nur Lesen“ – Modus

      void fooByValue(myStructure_t arg) {
          printf("passed by value %d %d\n", arg.a, arg.b);
          arg.a = 0;
      }
    2. Übergeben, indem Zeiger. Kopieren der Adresse der Variablen übergeben wird (also ja, es ist noch immer durch Wert übergeben, sondern übergeben Sie den Wert der Adresse, nicht der ganze argument). Also das ist wie „Lesen und schreiben“ – Modus. Wie Sie kann der Zugriff auf die übergebene variable durch seine-Adresse, Sie kann verändern Sie den Wert für diese variable außerhalb der Funktion.

      void fooByPtr(myStructure_t *arg) {
          printf("passed by pointer %d %d\n", arg->a, arg->b);
          arg->a = 0;
      }

      Aber! Sie kann immer noch nicht ändern Zeiger.

    3. Also, wenn Sie ändern möchten Zeiger, dann sollten Sie pass Zeiger auf Zeiger. Das ist wie „Lesen-schreiben ändern“ – Modus:

      void fooByDblPtr(myStructure_t **arg) {
          *arg = (myStructure_t*) malloc(sizeof(myStructure_t));
          (*arg)->a = 10;
          (*arg)->b = 20;
      }

      Wenn das war nur Zeiger dann wäre es ja ein memory leak:

      void fooByDblPtr(myStructure_t *arg) {
          arg = (myStructure_t*) malloc(sizeof(myStructure_t));
          (arg)->a = 10;
          (arg)->b = 20;
      }

      weil hier ordnen Sie die neue Adresse der lokalen Kopie der Adresse aus, und dieses argument zerstört werden würde nach der Funktion Abschluss.

      UPD. Zum Beispiel haben wir

      void fooByPtr(myStructure_t *arg) {
          printf("addr inside foo before %p\n", arg);
          arg = (myStructure_t*) malloc(sizeof(myStructure_t));
          (arg)->a = 10;
          (arg)->b = 20;
          printf("addr inside foo after %p\n", arg);
      }
      
      void main() {
          myStructure_t *x = NULL;
          x = malloc(sizeof(myStructure_t));
          x->a = 10;
          x->b = 20;
          printf("x addr before = %p\n", x);
          fooByPtr(x);
          printf("x addr after = %p\n", x);
          free(x);
      }

      innerhalb der Funktion Speicherplatz reserviert und der Zeiger zugeordnet ist, die lokale variable. Anrufer hält immer noch den alten Wert. Nach dem Funktionsaufruf verlieren wir die Adresse von dem Speicher, so kann es nicht veröffentlicht werden.

      Kurzes Fazit: es gibt eine einfache Regel – wenn nötig-zu ändern argument, pass Zeiger darauf. Also, wenn Sie möchten, zu ändern, Zeiger, übergeben Zeiger auf Zeiger. Wenn Sie wollen, Doppel-Wechsel-Zeiger übergeben Zeiger auf Zeiger auf Zeiger.

    4. Passing argument von pointer ist auch viel schneller, weil Sie nicht brauchen, um kopieren Sie alle Wert auf dem stack (natürlich, wenn der Wert größer ist als Zeiger auf diesen Wert, ansonsten übergeben, indem Zeiger für die read-only ist sinnlos). Aber es ist gefährlich, weil es könnte geändert werden, innerhalb der Funktion. So können Sie sicher argument definieren Sie mit dem Schlüsselwort const

      void constFoo(const myStructure_t *arg) {
          arg->a = 10;    //compilation error
          arg->b = 20;    //compilation error
      }

      Dies ist wirklich hilfreich, wenn Sie die Arbeit mit 3rd-party-Bibliotheken: Funktion Unterschrift erfahren Sie, ob die Funktion ändern kann, ist dein argument oder nicht. Obwohl const ist optional, ist es angebracht zu schreiben Schlüsselwort const jeder Zeit ist es möglich

    5. Array übergeben. In der Regel sendet auch array-Größe (daher, size_t) als argument. Array als Zeiger.

      void foo (int *buf, size_t nbuf) {
          ....
      }

      Manchmal findet man code, in dem Entwickler sendet Zeiger auf Objekt statt array, zum Beispiel

      void foo (int *buf, size_t size) {
          size_t i;
          for (i = 0; i < size; i++) {
              printf("%d ", buf[i]);
          }
      }
      
      int main(int argc, char **argv) {
          int a = 10;
          int buf[1] = { 10 };
          foo(buf, 1);
          foo(&a, 1);
      }

      In diesem Fall array mit einem element und Zeiger-auf-element Verhalten sich ähnlich (obwohl, Sie sind nicht das gleiche).

    • Hi Ivan, es ist das gleiche mit meinem Kommentar zu Ed oben, werden Sie sagen, dass ein „ändern“ ist im wesentlichen eine änderung der Struktur verweist im Speicher, solange wir uns nicht erlaubt, dies zu tun mit einem einzigen Zeiger?
    • Sehr Schöne Erklärung.
    • Ausgezeichnet, nur die Zusammenfassung, die ich brauchte !
  2. 3

    mit einem normalen parameter, sagen int erhalten Sie eine lokale Kopie

    mit einem Zeiger-parameter, sagen int* können Sie ändern, was es Punkte

    mit einem Doppel-pointer-parameter, sagen int** können Sie ändern, der pointer selbst, dh ‚repoint‘ es.

  3. 1

    Fügen Sie eine weitere Funktion:

    void modifyMe0(struct myStructure param1)
    {
        param1.a = 7;
    }

    Dieser geht die Struktur durch einen Wert. Die änderung in der Funktion ist, spiegelt sich nicht in dem argument übergeben modifyMe0().

    Hinzufügen aufrufende code wie dieser:

    printf("Before 0: a = %d, b = %d\n", test1->a, test1->b);
    
    modifyMe0(*test1);
    
    printf("After  0: a = %d, b = %d\n", test1->a, test1->b);

    Beachten Sie, dass die vorher-und nachher-Werte im aufrufenden code sind die gleichen. Man könnte auch hinzufügen, drucken auf Ihrem modifyMeN() Funktionen, um zu demonstrieren, dass in Ihnen der Wert geändert wird.

    Wenn Sie übergeben einen Zeiger auf die Struktur, die an die aufgerufene Funktion den Wert der Struktur, in der aufrufenden Funktion geändert werden kann. Wenn Sie übergeben Sie die Struktur der aufgerufenen Funktion als Wert, der Wert der Struktur, in der aufrufenden Funktion nicht verändert wird.

    Können Sie eine andere Funktion:

    void modifyMe3(struct myStructure **p1)
    {
        free(*p1);
        *p1 = malloc(sizeof(*p1));
        (*p1)->a = -3;
        (*p1)->b = -6;
    }

    Hinzufügen aufrufende code wie dieser:

    printf("Before 3: address = %p, a = %d, b = %d\n", (void *)test1, test1->a, test1->b);
    
    modifyMe0(*test1);
    
    printf("After  3: address = %p, a = %d, b = %d\n", (void *)test1, test1->a, test1->b);

    Beachten Sie, dass die Adresse der Struktur hat sich verändert nach dem Aufruf modifyMe3().

Kostenlose Online-Tests

Letzte Fragen

Tun ItemView löst Blase?

Ich habe eine CompositeView für eine Tabelle. Ich habe Trigger-set in der Kind-ItemView für jede Zeile... var TableRow = Marionette.ItemView.extend({ tagName:...

Wie kann ich untersuchen, WCF was 400 bad request über GET?

Die folgenden WCF-endpoint funktioniert gut mit dem WCF test client: AssetList ListFlaggedAssets(short processCode, string platform, string endpoint = "null", string portalId = "null", int...

Bei der Verwendung von UUIDs, sollte ich auch mit AUTO_INCREMENT?

Wir bauen eine neue web-app, die eine offline-iPad - /Android-app-version auf einer Reihe von lokalen Geräten, die Einsätze mit neuen Daten. Als solche benötigen...

Actionscript-Objekt, das verschiedene Eigenschaften

Wie kann ich die Anzahl der Eigenschaften in einer generischen Actionscript-Objekt? (Wie die Array-Länge) InformationsquelleAutor Fragsworth | 2011-01-15

Wie plot mehrere Graphen und nutzen Sie die Navigations-Taste im [matplotlib]

Die neueste version von matplotlib erstellt automatisch Navigations-buttons unter den graph. Aber die Beispiele, die ich finden alles im Internet zeigen, wie erstellen Sie...