Utilizzo dell'operatore freccia (->) in C


257

Sto leggendo un libro intitolato "Teach Yourself C in 21 Days" (ho già imparato Java e C #, quindi mi sto muovendo ad un ritmo molto più veloce). Stavo leggendo il capitolo sui puntatori e l' operatore-> (freccia) si avvicinò senza spiegazioni. Penso che sia usato per chiamare membri e funzioni (come l'equivalente dell'operatore (punto), ma per i puntatori anziché i membri). Ma non ne sono del tutto sicuro..

Potrei per favore ottenere una spiegazione e un esempio di codice?


90
Ottieni un libro migliore. norvig.com/21-days.html
joshperry

9
qrdl è corretto: i libri "Impara X in Y giorni" sono generalmente spazzatura. Oltre a K&R, consiglierei anche "C Primer Plus" di Prata, che va più in profondità di K&R.
J. Taylor,

3
@Steve Quella domanda riguarda C ++. Chiamarlo una confusione per me quando ho iniziato a leggere sul sovraccarico dell'operatore in quell'altra risposta, che non è rilevante in C.
Johann,

1
@Belton Le serie difficili sono brutte, il ragazzo dice cose che non erano nemmeno rilevanti quando ha scritto il libro e non gli importa delle buone pratiche.
Bálint,

1
Il link di Peter Norvig a "Insegnare a programmare da soli in dieci anni" è fantastico, uno dei miei preferiti. Ecco la versione comica che spiega come farlo in 21 giorni, che purtroppo ho ricordato come un XKCD ma mi sbagliavo: abstrusegoose.com/249
Ted

Risposte:


462

foo->barè equivalente a (*foo).bar, cioè ottiene il membro chiamato bardalla struttura che foopunta a.


51
Vale la pena notare che se l'operatore di dereference fosse stato postfisso, come in Pascal, l' ->operatore non sarebbe stato affatto necessario, poiché sarebbe stato equivalente a molto più leggibile foo*.bar. Anche il caos delle funzioni di battitura a macchina con tutte le parentesi extra sarebbe stato evitato.
Marchese di Lorne,

1
Quindi, foo*.bared (*foo).barentrambi equivarrebbero a foo->bar? Che dire Foo myFoo = *foo; myFoo.bar?
Aaron Franke,

9
No, sta solo dicendo che se i creatori di C avrebbero reso l'operatore di dereference come operatore POSTfix invece di PREfix, sarebbe stato più facile. Ma è un operatore di prefisso in C.
Reichhart

@ user207421 Ti prego di fornire una breve descrizione o un collegamento alle "funzioni di battitura a macchina con tutte le parentesi extra" che hai citato? Grazie.
RoG

1
@ user207421 nah, causerebbe più genitori .. finora, c'è la priorità di () e [] a destra sopra * a sinistra. se sono tutti da una parte, avrai messo più genitori. Lo stesso nelle espressioni, a causa del conflitto con l'operatore di moltiplicazione. Pascal ^ potrebbe essere un'opzione, ma è stato riservato per l'operazione bit, ancora più genitori.
Swift - Friday Pie,

130

Sì è quello.

È solo la versione punto quando si desidera accedere agli elementi di una struttura / classe che è un puntatore anziché un riferimento.

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;

Questo è tutto!


3
Poiché pvar non è inizializzato, come lo inizializzereste se voleste che pvar indichi una nuova struttura, non lo è pvar = &var?
CMCDragonkai,

La domanda riguardava specificamente C, che non ha classi o variabili di riferimento.
Oak,

1
hmm non dovresti fare un malloc prima di scrivere a pvar struct foo * pvar; ?? pvar-> y scrivi nello spazio non allocato!
Zibri

Inizializzazione pvar: inizializza manualmente tutti i membri su alcune impostazioni predefinite che vuoi avere o usa qualcosa come calloc () se il riempimento zero andrebbe bene per te.
Reichhart,

2
non dovrebbe essere: pvar = malloc (sizeof (struct foo)) o malloc (sizeof (* pvar)) ??
Yuri Aps,

33

a->bè l'abbreviazione (*a).bin tutti i modi (lo stesso per le funzioni: a->b()è l'abbreviazione di (*a).b()).


1
c'è documentazione che dice che funziona così anche per i metodi?
AsheKetchum,

28

Aggiungerei alle risposte il "perché?".

.è un operatore di accesso membro standard che ha una precedenza maggiore rispetto *all'operatore puntatore.

Quando stai provando ad accedere agli interni di una struct e l'hai scritto come *foo.barallora il compilatore penserebbe di voler un elemento "bar" di "pippo" (che è un indirizzo in memoria) e ovviamente quel mero indirizzo non ha alcun membro.

Quindi è necessario chiedere al compilatore di fare prima la dereference con (*foo)e poi accedere all'elemento member:, (*foo).barche è un po 'goffo da scrivere, quindi i bravi ragazzi hanno escogitato una versione abbreviata: foo->barche è una sorta di accesso ai membri da parte dell'operatore puntatore.


19

foo->barè solo una scorciatoia per (*foo).bar. Questo è tutto quello che c'è da fare.


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

Qui il per accedere ai valori di ie jpossiamo utilizzare la variabile ae il puntatore pcome segue: a.i, (*p).ie p->isono tutti uguali.

Ecco .un "Selettore diretto" ed ->è un "Selettore indiretto".


2

Beh, devo aggiungere anche qualcosa. La struttura è leggermente diversa dall'array perché l'array è un puntatore e la struttura no. Perciò stai attento!

Diciamo che scrivo questo inutile pezzo di codice:

#include <stdio.h>

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

int main(void){

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

}

Qui il puntatore ptrpunta all'indirizzo ( ! ) Della variabile struttura audima accanto alla struttura indirizzo ha anche una porzione di dati ( ! )! Il primo membro del blocco di dati ha lo stesso indirizzo della struttura stessa e puoi ottenere i suoi dati dereferenziando solo un puntatore come questo *ptr (senza parentesi graffe) .

Ma se si vuole acess qualsiasi altro membro rispetto al primo, è necessario aggiungere un designatore come .km, .kph, .kgche non sono altro che compensa per l'indirizzo di base del blocco di dati ...

Ma a causa della precarietà non è possibile scrivere *ptr.kgpoiché l'operatore di accesso .viene valutato prima dell'operatore di dereference *e si otterrebbe ciò *(ptr.kg)che non è possibile in quanto il puntatore non ha membri! E il compilatore lo sa e quindi emetterà un errore, ad esempio:

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

Invece si usa questo (*ptr).kge si forza il compilatore a 1 ° dereference il puntatore e si abilita l'accesso al blocco di dati e 2 ° si aggiunge un offset (designatore) per scegliere il membro.

Controlla questa immagine che ho creato:

inserisci qui la descrizione dell'immagine

Ma se avessi membri nidificati questa sintassi diventerebbe illeggibile e quindi è ->stata introdotta. Penso che la leggibilità sia l'unica ragione giustificabile per usarlo in quanto ptr->kgè molto più facile da scrivere rispetto a (*ptr).kg.

Ora scriviamo questo in modo diverso in modo da vedere più chiaramente la connessione. (*ptr).kg(*&audi).kgaudi.kg. Qui ho usato per la prima volta il fatto che si ptrtratta di un "indirizzo di audi", vale a dire il &audifatto che gli operatori di "riferimento" & e "dereference" si * annullano a vicenda.


1

Ho dovuto apportare una piccola modifica al programma di Jack per farlo funzionare. Dopo aver dichiarato il puntatore struct pvar, puntarlo all'indirizzo var. Ho trovato questa soluzione a pagina 242 della programmazione di Stephen Kochan 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;
}

Esegui questo in vim con il seguente comando:

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

Uscita:

5 - 14.30
6 - 22.40

3
suggerimento vim: utilizzare %per rappresentare il nome file corrente. In questo modo:!gcc % && ./a.out
Gibuti,

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

L' ->operatore rende il codice più leggibile *dell'operatore in alcune situazioni.

Come: (citato dal progetto EDK II )

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;

};

La _EFI_BLOCK_IO_PROTOCOLstruttura contiene 4 membri del puntatore a funzione.

Supponiamo di avere una variabile struct _EFI_BLOCK_IO_PROTOCOL * pStructe di voler usare il buon vecchio *operatore per chiamare il suo puntatore alla funzione membro. Finirai con un codice come questo:

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

Ma con l' ->operatore puoi scrivere in questo modo:

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

Quale sembra migliore?


1
Penso che il codice sarebbe più leggibile se non fosse tutto in maiuscolo come è stato digitato dagli adolescenti nella chat AOL degli anni '90.
Grazie

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);
}

l'uscita è 5 5 5


0

Dot è un operatore di dereference e viene utilizzato per collegare la variabile di struttura per un particolare record di struttura. Per esempio :

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

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

In tal modo possiamo usare un operatore punto per accedere alla variabile struttura


6
Quale valore aggiunge? L'esempio è un po 'scarso rispetto alle altre risposte che lo confrontano effettivamente ->. Anche a questa domanda è già stata data risposta da 4,5 anni.
EWit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.