Utilisation de l'opérateur flèche (->) en C

257

Je lis un livre intitulé "Teach Yourself C in 21 Days" (j'ai déjà appris Java et C # donc je me déplace à un rythme beaucoup plus rapide). Je lisais le chapitre sur les pointeurs et l' opérateur-> (flèche) est apparu sans explication. Je pense qu'il est utilisé pour appeler des membres et des fonctions (comme l'équivalent de l' opérateur (point), mais pour des pointeurs au lieu de membres). Mais je ne suis pas tout à fait sûr..

Puis-je obtenir une explication et un exemple de code?

Mohit Deshpande
la source
90
Obtenez un meilleur livre. norvig.com/21-days.html
joshperry
9
qrdl est correct - les livres "Learn X in Y days" sont généralement des ordures. En plus de K&R, je recommanderais également le "C Primer Plus" de Prata, qui va plus en profondeur que K&R.
J. Taylor
3
@Steve Cette question concerne le C ++. L'appeler un a causé une certaine confusion pour moi lorsque j'ai commencé à lire sur la surcharge de l'opérateur dans cette autre réponse, qui n'est pas pertinente en C.
Johann
1
@Belton Les séries difficiles sont mauvaises, le gars dit des choses qui n'étaient même pas pertinentes quand il a écrit le livre et il ne se soucie pas des bonnes pratiques.
Bálint
1
Le lien de Peter Norvig vers "Teach Yourself Programming in Ten Years" est génial, l'un de mes favoris. Voici la version comique qui explique comment faire cela en 21 jours, dont je me suis malheureusement souvenu en tant que XKCD mais j'avais tort: abstrusegoose.com/249
Ted

Réponses:

462

foo->barest équivalent à (*foo).bar, c'est- à -dire qu'il obtient le membre appelé à barpartir de la structure qui foopointe vers.

sepp2k
la source
51
Il convient de noter que si l'opérateur de déréférencement avait été rendu postfixé, comme dans Pascal, l' ->opérateur n'aurait pas été nécessaire du tout, car il aurait été équivalent à celui beaucoup plus lisible foo*.bar. Tout le gâchis des fonctions de typage avec toutes les parenthèses supplémentaires aurait également été évité.
Marquis de Lorne
1
Alors, foo*.baret les (*foo).bardeux seraient-ils équivalents foo->bar? Et alors Foo myFoo = *foo; myFoo.bar?
Aaron Franke
9
Non, il dit simplement que SI les créateurs de C auraient fait de l'opérateur de déréférencement un opérateur POSTfix au lieu de PREfix, cela aurait été plus facile. Mais c'est un opérateur de préfixe en C.
reichhart
@ user207421 Coulfd, veuillez donner une brève description ou un lien vers les "fonctions de saisie avec toutes les parenthèses supplémentaires" que vous mentionnez? Merci.
RoG
1
@ user207421 nah, cela causerait plus de parents .. jusqu'à présent, il y a priorité de () et [] à droite au dessus * à gauche. s'ils sont tous d'un côté, vous aurez mis plus de parents. Idem dans les expressions, en raison d'un conflit avec l'opérateur de multiplication. Pascal ^ pourrait être une option mais il était réservé au fonctionnement en bits, encore plus de parents.
Swift - Friday Pie
130

Oui c'est ça.

C'est juste la version dot lorsque vous souhaitez accéder aux éléments d'une structure / classe qui est un pointeur au lieu d'une référence.

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;

C'est tout!

Jack
la source
3
Puisque pvar n'est pas initialisé, comment l'initialiseriez-vous si vous vouliez que pvar pointe vers une nouvelle structure, ce n'est pas le cas pvar = &var?
CMCDragonkai
La question portait spécifiquement sur C, qui n'a ni classes ni variables de référence.
Oak
1
hmm ne devriez-vous pas faire un malloc avant d'écrire dans pvar struct foo * pvar; ?? pvar-> y écrivez dans l'espace non alloué!
Zibri
initialisation pvar: initialisez tous les membres manuellement à certaines valeurs par défaut que vous souhaitez avoir ou utilisez quelque chose comme calloc () si le remplissage zéro vous convient.
reichhart
2
ne devrait-il pas être: pvar = malloc (sizeof (struct foo)) ou malloc (sizeof (* pvar)) ??
Yuri Aps
33

a->best juste court (*a).bdans tous les sens (idem pour les fonctions: a->b()est court pour (*a).b()).

Peter Alexander
la source
1
existe-t-il une documentation qui indique que cela fonctionne également de cette façon pour les méthodes?
AsheKetchum
28

Je voudrais juste ajouter aux réponses le "pourquoi?".

.est un opérateur d'accès aux membres standard qui a une priorité plus élevée que l' *opérateur de pointeur.

Lorsque vous essayez d'accéder aux éléments internes d'une structure et que vous l'avez écrit comme *foo.baralors, le compilateur penserait vouloir un élément `` bar '' de `` foo '' (qui est une adresse en mémoire) et, évidemment, cette simple adresse n'a pas de membres.

Ainsi, vous devez demander au compilateur de déréférencer d'abord avec (*foo)puis accéder à l'élément membre:, (*foo).barce qui est un peu maladroit à écrire, de sorte que les bonnes personnes ont trouvé une version abrégée: foo->barqui est une sorte d'accès aux membres par opérateur de pointeur.

Lukasz Matysiak
la source
19

foo->barn'est qu'un raccourci pour (*foo).bar. C'est tout ce qu'on peut en dire.

Matti Virkkunen
la source
10
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

Ici , l'accès aux valeurs de iet jnous pouvons utiliser la variable aet le pointeur pcomme suit: a.i, (*p).iet p->isont tous les mêmes.

Voici .un "sélecteur direct" et ->un "sélecteur indirect".

Jayghosh Wankar
la source
2

Je dois également ajouter quelque chose. La structure est un peu différente du tableau car le tableau est un pointeur et la structure ne l'est pas. Donc sois prudent!

Disons que j'écris ce morceau de code inutile:

#include <stdio.h>

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

int main(void){

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

}

Ici, le pointeur ptrpointe vers l'adresse ( ! ) De la variable de structure audimais à côté de la structure d'adresse a également un morceau de données ( ! )! Le premier membre du bloc de données a la même adresse que la structure elle-même et vous pouvez obtenir ses données en ne déréférençant qu'un pointeur comme celui-ci *ptr (pas d'accolades) .

Mais si vous voulez acess tout autre membre que le premier, vous devez ajouter un code comme .km, .kph, .kgqui ne sont rien de plus que des compensations à l'adresse de base du bloc de données ...

Mais en raison de la priorité, vous ne pouvez pas écrire *ptr.kgcar l'opérateur d'accès .est évalué avant l'opérateur de déréférence *et vous obtiendrez *(ptr.kg)ce qui n'est pas possible car le pointeur n'a pas de membres! Et le compilateur le sait et émettra donc une erreur, par exemple:

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

Au lieu de cela vous utilisez ce (*ptr).kget vous forcer compilateur 1er déréférencer le pointeur et activez acess au bloc de données et 2 vous ajoutez un décalage (désignateur) de choisir le membre.

Vérifiez cette image que j'ai faite:

entrez la description de l'image ici

Mais si vous aviez des membres imbriqués, cette syntaxe deviendrait illisible et a donc ->été introduite. Je pense que la lisibilité est la seule raison justifiable de l'utiliser, car elle ptr->kgest beaucoup plus facile à écrire qu'à (*ptr).kg.

Maintenant, écrivons ceci différemment afin que vous puissiez voir la connexion plus clairement. (*ptr).kg(*&audi).kgaudi.kg. Ici, j'ai d'abord utilisé le fait qu'il ptrs'agit d'une "adresse de audi" c'est &audi-à- dire le fait que les opérateurs "référence" & et "déréférencement" * s'annulent.

71GA
la source
1

J'ai dû apporter un petit changement au programme de Jack pour le faire fonctionner. Après avoir déclaré le pointeur struct pvar, pointez-le sur l'adresse de var. J'ai trouvé cette solution à la page 242 de la programmation de Stephen Kochan en 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;
}

Exécutez cela dans vim avec la commande suivante:

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

Sortira:

5 - 14.30
6 - 22.40
Rich Vogt
la source
3
astuce vim: utilisez %pour représenter le nom de fichier actuel. Comme ça:!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;
}
Gopal Rao
la source
1

L' ->opérateur rend le code plus lisible que le* opérateur dans certaines situations.

Tels que: (cité du projet 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;

};

le _EFI_BLOCK_IO_PROTOCOL structure contient 4 membres de pointeur de fonction.

Supposons que vous ayez une variable struct _EFI_BLOCK_IO_PROTOCOL * pStructet que vous souhaitiez utiliser le bon ancien *opérateur pour appeler son pointeur de fonction membre. Vous vous retrouverez avec un code comme celui-ci:

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

Mais avec l' ->opérateur, vous pouvez écrire comme ceci:

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

Qui a l'air mieux?

smwikipedia
la source
1
Je pense que le code serait plus lisible s'il n'est pas dans toutes les majuscules comme il est tapé par des adolescents sur le chat AOL des années 90.
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);
}

la sortie est de 5 5 5

prashanth
la source
0

Le point est un opérateur de déréférence et utilisé pour connecter la variable de structure pour un enregistrement particulier de la structure. Par exemple :

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

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

De cette façon, nous pouvons utiliser un opérateur point pour accéder à la variable de structure

divya
la source
6
Quelle valeur cela ajoute-t-il? L'exemple est un peu pauvre par rapport aux autres réponses qui le comparent réellement ->. Cette question a également été répondue depuis 4,5 ans déjà.
EWit