web-dev-qa-db-fra.com

C ++: différence entre l'esperluette "&" et l'astérisque "*" dans la déclaration de fonction / méthode?

Y a-t-il une sorte de différence subtile entre ceux-ci:

void a1(float &b) {
    b=1;
};
a1(b);

et

void a1(float *b) {
    (*b)=1;
};
a1(&b);

?

Ils font tous les deux la même chose (ou du moins il semble de main ()), mais le premier est évidemment plus court, cependant la plupart du code que je vois utilise la deuxième notation. Y a-t-il une différence? Peut-être au cas où ce serait un objet au lieu de flotter?

65
Slava V

Les deux font de même, mais on utilise des références et on utilise des pointeurs.

Voir ma réponse ici pour une liste complète de toutes les différences .

52
Brian R. Bondy

Oui. Le * la notation indique que ce qui est transmis sur la pile est un pointeur, c'est-à-dire l'adresse de quelque chose. Le & dit que c'est une référence. L'effet est similaire mais pas identique:

Prenons deux cas:

   void examP(int* ip);
   void examR(int& i);

   int i;

Si j'appelle examP, j'écris

   examP(&i);

qui prend l'adresse de l'article et la passe sur la pile. Si j'appelle examR,

   examR(i);

Je n'en ai pas besoin; maintenant le compilateur passe "en quelque sorte" une référence - ce qui signifie pratiquement qu'il obtient et passe l'adresse de i. Côté code, puis

   void examP(int* ip){
        *ip += 1;
   }

Je dois m'assurer de déréférencer le pointeur. ip += 1 fait quelque chose de très différent.

   void examR(int& i){
        i += 1;
   }

met toujours à jour la valeur de i.

Pour en savoir plus, lisez "appel par référence" et "appel par valeur". Le & la notion donne l'appel C++ par référence.

23
Charlie Martin

Dans le premier exemple avec des références, vous savez que b ne peut pas être NULL. Avec l'exemple de pointeur, b peut être le pointeur NULL.

Cependant, notez qu'il est possible de passer un objet NULL à travers une référence, mais c'est maladroit et la procédure appelée peut supposer que c'est une erreur de l'avoir fait:

a1(*(float *)NULL);
5
Greg Hewgill

Dans le deuxième exemple, le appelant doit préfixer le nom de la variable avec '&' pour transmettre l'adresse de la variable.

Cela peut être un avantage - l'appelant ne peut pas modifier une variable par inadvertance en la passant comme référence alors qu'il pensait passer par valeur.

3
finnw

Mis à part le sucre syntaxique, la seule vraie différence est la possibilité pour un paramètre de fonction qui est un pointeur d'être nul. Ainsi, la version du pointeur peut être plus expressive si elle gère correctement la casse nulle. Le cas nul peut également avoir une signification particulière. La version de référence ne peut fonctionner que sur des valeurs du type spécifié sans capacité nulle.

3
jmucchiello

Fonctionnellement dans votre exemple, les deux versions font de même.

Le premier a l'avantage d'être transparent côté appel. Imaginez à quoi cela ressemblerait pour un opérateur:

cin >> &x;

Et à quoi cela ressemble moche pour une invocation de swap

swap(&a, &b);

Vous voulez échanger a et b. Et cela semble beaucoup mieux que lorsque vous devez d'abord prendre l'adresse. Par ailleurs, bjarne stroustrup écrit que la principale raison des références était la transparence qui a été ajoutée du côté de l'appel - en particulier pour les opérateurs. Voir aussi comment il n'est plus évident de savoir si les éléments suivants

&a + 10

Ajouterait 10 au contenu de a, appelant l'opérateur + de celui-ci, ou s'il ajoute 10 à un pointeur temporaire sur a. Ajoutez cela à l'impossibilité de ne pas surcharger les opérateurs uniquement pour les opérandes intégrés (comme un pointeur et un entier). Les références rendent ce cristal clair.

Les pointeurs sont utiles si vous voulez pouvoir mettre un "null":

a1(0);

Ensuite, en a1, la méthode peut comparer le pointeur avec 0 et voir si le pointeur pointe vers un objet.

1

Une grande différence à noter est ce qui se passe à l'extérieur, vous avez soit:

a1(something);

ou:

a1(&something);

J'aime passer des arguments par référence (toujours const un :)) quand ils ne sont pas modifiés dans la fonction/méthode (et alors vous pouvez aussi passer des objets automatiques/temporaires à l'intérieur) et les passer par pointeur pour signifier et alerter l'utilisateur/lecteur du code appelant la méthode selon laquelle l'argument peut et probablement est intentionnellement modifié à l'intérieur.

0
RnR