Verwendung des Pfeiloperators (->) in C.


257

Ich lese ein Buch mit dem Titel "Teach Yourself C in 21 Days" (ich habe bereits Java und C # gelernt, daher bewege ich mich viel schneller). Ich habe das Kapitel über Zeiger gelesen und der Operator-> (Pfeil) wurde ohne Erklärung angezeigt. Ich denke, dass es verwendet wird, um Mitglieder und Funktionen aufzurufen (wie das Äquivalent des (Punkt-) Operators, aber für Zeiger anstelle von Mitgliedern). Ich bin mir aber nicht ganz sicher..

Könnte ich bitte eine Erklärung und ein Codebeispiel bekommen?


90
Holen Sie sich ein besseres Buch. norvig.com/21-days.html
Joshperry

9
qrdl ist richtig - die Bücher "X in Y Tagen lernen" sind im Allgemeinen Müll. Neben K & R würde ich auch Pratas "C Primer Plus" empfehlen, der tiefer geht als K & R.
J. Taylor

3
@Steve Diese Frage befasst sich mit C ++. Das als "a" zu bezeichnen, verursachte einige Verwirrung für mich, als ich anfing, über die Überlastung des Bedieners in dieser anderen Antwort zu lesen, die in C.
Johann

1
@Belton Die Hard-Way-Serien sind schlecht, der Typ sagt Dinge, die nicht einmal relevant waren, als er das Buch schrieb, und er kümmert sich nicht um gute Praktiken.
Bálint

1
Der Peter Norvig-Link zu "Teach Yourself Programming in Ten Years" ist großartig, einer meiner Favoriten. Hier ist die Comic-Version, die erklärt, wie man das in 21 Tagen macht, an die ich mich leider als XKCD erinnerte, aber ich habe mich geirrt: abstrusegoose.com/249
Ted

Antworten:


462

foo->barist äquivalent zu (*foo).bar, dh es wird das aufgerufene Mitglied baraus der Struktur aufgerufen, die fooauf zeigt.


51
Es ist erwähnenswert, dass, wenn der Dereferenzierungsoperator wie in Pascal postfixiert worden wäre, der ->Operator überhaupt nicht benötigt worden wäre, da er dem viel besser lesbaren äquivalent gewesen wäre foo*.bar. Das ganze Durcheinander von Schreibfunktionen mit all den zusätzlichen Klammern wäre ebenfalls vermieden worden.
Marquis von Lorne

1
Also wäre foo*.barund (*foo).barbeides gleichbedeutend mit foo->bar? Was ist mit Foo myFoo = *foo; myFoo.bar?
Aaron Franke

9
Nein, er sagt nur IF die Schöpfer von C haben den Dereferenzierungsoperator als Postfix - Operator statt Vorsilbe gemacht hätten , dann wäre es gewesen leichter. Aber es ist ein Präfix-Operator in C.
Reichhart

@ user207421 Könnten Sie bitte eine kurze Beschreibung oder einen Link zu den von Ihnen erwähnten "Schreibfunktionen mit allen zusätzlichen Klammern" geben? Vielen Dank.
RoG

1
@ user207421 nah, es würde mehr Eltern verursachen .. bisher gibt es Priorität von () und [] rechts über * links. Wenn sie alle auf einer Seite sind, haben Sie mehr Eltern. Gleiches gilt für Ausdrücke aufgrund eines Konflikts mit dem Multiplikationsoperator. Pascal ^ könnte eine Option sein, aber es war für die Bitoperation reserviert, noch mehr Eltern.
Swift - Friday Pie

129

Ja das ist es.

Es ist nur die Punktversion, wenn Sie auf Elemente einer Struktur / Klasse zugreifen möchten, die ein Zeiger anstelle einer Referenz ist.

struct foo
{
  int x;
  float y;
};

struct foo var;
struct foo* pvar;
pvar = malloc(sizeof(pvar));

var.x = 5;
(&var)->y = 14.3;
pvar->y = 22.4;
(*pvar).x = 6;

Das ist es!


3
Da pvar nicht initialisiert ist, wie würden Sie es initialisieren, wenn pvar auf eine neue Struktur verweisen soll, das heißt nicht pvar = &var?
CMCDragonkai

Die Frage betraf speziell C, das keine Klassen oder Referenzvariablen enthält.
Eiche

1
hmm solltest du kein malloc machen bevor du an pvar struct foo * pvar schreibst; ?? pvar-> y schreibe in nicht zugewiesenen Speicherplatz!
Zibri

pvar-Initialisierung: Initialisieren Sie alle Mitglieder manuell auf einige Standardeinstellungen, die Sie haben möchten, oder verwenden Sie etwas wie calloc (), wenn die Nullfüllung für Sie in Ordnung wäre.
Reichhart

2
sollte es nicht sein: pvar = malloc (sizeof (struct foo)) oder malloc (sizeof (* pvar)) ??
Yuri Aps

33

a->bist (*a).bin jeder Hinsicht nur kurz für (gleich für Funktionen: a->b()ist kurz für (*a).b()).


1
Gibt es eine Dokumentation, die besagt, dass dies auch für Methoden funktioniert?
AsheKetchum

28

Ich würde den Antworten nur das "Warum?" Hinzufügen.

.ist ein Standardzugriffsoperator für Mitglieder, der eine höhere Priorität als der *Zeigeroperator hat.

Wenn Sie versuchen, auf die Interna einer Struktur zuzugreifen, und Sie es so geschrieben haben, *foo.barwürde der Compiler denken, dass er ein 'bar'-Element von' foo '(eine Adresse im Speicher) haben möchte, und offensichtlich hat diese bloße Adresse keine Mitglieder.

Daher müssen Sie den Compiler bitten, zuerst eine Dereferenzierung mit (*foo)und dann auf das Element element: zuzugreifen. Dies (*foo).barist etwas umständlich zu schreiben, sodass die guten Leute eine Kurzversion entwickelt haben: foo->bareine Art Elementzugriff per Zeigeroperator.



10
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

Hier auf den Zugriff auf die Werte iund jwir können die Variable verwenden aund den Zeiger pwie folgt: a.i, (*p).iund p->isind alle gleich.

Hier .ist ein "Direktwähler" und ->ein "Indirekter Selektor".


2

Nun, ich muss auch etwas hinzufügen. Die Struktur unterscheidet sich ein wenig vom Array, da das Array ein Zeiger ist und die Struktur nicht. Also sei vorsichtig!

Nehmen wir an, ich schreibe diesen nutzlosen Code:

#include <stdio.h>

typedef struct{
        int km;
        int kph;
        int kg;
    } car;

int main(void){

    car audi = {12000, 230, 760};
    car *ptr = &audi;

}

Hier zeigt der Zeiger ptrauf die Adresse ( ! ) Der Strukturvariablen, hat audiaber neben der Adressstruktur auch einen Datenblock ( ! )! Das erste Mitglied des Datenblocks hat dieselbe Adresse wie die Struktur selbst, und Sie können die Daten abrufen, indem Sie nur einen Zeiger wie diesen dereferenzieren*ptr (keine geschweiften Klammern) .

Aber Wenn Sie ein anderes Mitglied als das erste Acess wollen, müssen Sie einen Bezeichner hinzufügen wie .km, .kph, .kgdie nichts mehr als Offsets auf die Basisadresse des Datenblock ...

Aufgrund der Vorrangstellung können Sie jedoch nicht schreiben, *ptr.kgda der Zugriffsoperator .vor dem Dereferenzierungsoperator ausgewertet wird *und Sie erhalten, *(ptr.kg)was nicht möglich ist, da der Zeiger keine Mitglieder hat! Und der Compiler weiß das und wird daher einen Fehler ausgeben, zB:

error: ptr is a pointer; did you mean to use ‘->’?
  printf("%d\n", *ptr.km);

Stattdessen verwenden Sie diese (*ptr).kgund Sie Compiler erzwingen 1. den Zeiger dereferenzieren und ermöglichen acess zum Teil der Daten und zweiten Sie einen Offset (Bezeichner) in dem das Element zu wählen.

Überprüfen Sie dieses Bild, das ich gemacht habe:

Geben Sie hier die Bildbeschreibung ein

Wenn Sie jedoch verschachtelte Mitglieder hätten, wäre diese Syntax unlesbar und wurde daher ->eingeführt. Ich denke, Lesbarkeit ist der einzige berechtigte Grund für die Verwendung, da dies ptr->kgviel einfacher zu schreiben ist als(*ptr).kg .

Lassen Sie uns dies jetzt anders schreiben, damit Sie den Zusammenhang klarer sehen. (*ptr).kg(*&audi).kgaudi.kg. Hier habe ich zuerst die Tatsache verwendet, dass ptres sich um eine "Adresse von audi" handelt, dh die &audiTatsache, dass "Referenz" & und "Dereferenzierung" * -Operatoren gegenseitig aufheben.


1

Ich musste eine kleine Änderung an Jacks Programm vornehmen, damit es lief. Zeigen Sie nach dem Deklarieren des Strukturzeigers pvar auf die Adresse von var. Ich fand diese Lösung auf Seite 242 von Stephen Kochans Programmierung in C.

#include <stdio.h>

int main()
{
  struct foo
  {
    int x;
    float y;
  };

  struct foo var;
  struct foo* pvar;
  pvar = &var;

  var.x = 5;
  (&var)->y = 14.3;
  printf("%i - %.02f\n", var.x, (&var)->y);
  pvar->x = 6;
  pvar->y = 22.4;
  printf("%i - %.02f\n", pvar->x, pvar->y);
  return 0;
}

Führen Sie dies in vim mit dem folgenden Befehl aus:

:!gcc -o var var.c && ./var

Wird ausgegeben:

5 - 14.30
6 - 22.40

3
vim Tipp: Verwenden Sie diese Option %, um den aktuellen Dateinamen darzustellen. Wie so:!gcc % && ./a.out
Jibberia

1
#include<stdio.h>

int main()
{
    struct foo
    {
        int x;
        float y;
    } var1;
    struct foo var;
    struct foo* pvar;

    pvar = &var1;
    /* if pvar = &var; it directly 
       takes values stored in var, and if give  
       new > values like pvar->x = 6; pvar->y = 22.4; 
       it modifies the values of var  
       object..so better to give new reference. */
    var.x = 5;
    (&var)->y = 14.3;
    printf("%i - %.02f\n", var.x, (&var)->y);

    pvar->x = 6;
    pvar->y = 22.4;
    printf("%i - %.02f\n", pvar->x, pvar->y);

    return 0;
}

1

Der ->Operator macht den Code lesbarer als den* in einigen Situationen Operator.

Wie zum Beispiel: (zitiert aus dem EDK II-Projekt )

typedef
EFI_STATUS
(EFIAPI *EFI_BLOCK_READ)(
  IN EFI_BLOCK_IO_PROTOCOL          *This,
  IN UINT32                         MediaId,
  IN EFI_LBA                        Lba,
  IN UINTN                          BufferSize,
  OUT VOID                          *Buffer
  );


struct _EFI_BLOCK_IO_PROTOCOL {
  ///
  /// The revision to which the block IO interface adheres. All future
  /// revisions must be backwards compatible. If a future version is not
  /// back wards compatible, it is not the same GUID.
  ///
  UINT64              Revision;
  ///
  /// Pointer to the EFI_BLOCK_IO_MEDIA data for this device.
  ///
  EFI_BLOCK_IO_MEDIA  *Media;

  EFI_BLOCK_RESET     Reset;
  EFI_BLOCK_READ      ReadBlocks;
  EFI_BLOCK_WRITE     WriteBlocks;
  EFI_BLOCK_FLUSH     FlushBlocks;

};

Das _EFI_BLOCK_IO_PROTOCOL Struktur enthält 4 Funktionszeigerelemente.

Angenommen, Sie haben eine Variable struct _EFI_BLOCK_IO_PROTOCOL * pStructund möchten den guten alten *Operator verwenden, um den Elementfunktionszeiger aufzurufen. Am Ende erhalten Sie folgenden Code:

(*pStruct).ReadBlocks(...arguments...)

Mit dem ->Operator können Sie jedoch folgendermaßen schreiben:

pStruct->ReadBlocks(...arguments...).

Welches sieht besser aus?


1
Ich denke, der Code wäre besser lesbar, wenn er nicht in Großbuchstaben geschrieben wäre, wie er von Teenagern im AOL-Chat aus den 90ern eingegeben wurde.
Thang

1
#include<stdio.h>
struct examp{
int number;
};
struct examp a,*b=&a;`enter code here`
main()
{
a.number=5;
/* a.number,b->number,(*b).number produces same output. b->number is mostly used in linked list*/
   printf("%d \n %d \n %d",a.number,b->number,(*b).number);
}

Ausgabe ist 5 5 5


0

Dot ist ein Dereferenzierungsoperator und wird verwendet, um die Strukturvariable für einen bestimmten Strukturdatensatz zu verbinden. Z.B :

struct student
    {
      int s.no;
      Char name [];
      int age;
    } s1,s2;

main()
    {
      s1.name;
      s2.name;
    }

Auf diese Weise können wir einen Punktoperator verwenden, um auf die Strukturvariable zuzugreifen


6
Welchen Wert bringt dies? Das Beispiel ist etwas schlecht im Vergleich zu den anderen Antworten, mit denen es tatsächlich verglichen wird ->. Auch diese Frage wird bereits seit 4,5 Jahren beantwortet.
EWit
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.