Placement de l'astérisque dans les déclarations de pointeur

92

J'ai récemment décidé que je devais enfin apprendre le C / C ++, et il y a une chose que je ne comprends pas vraiment à propos des pointeurs ou plus précisément de leur définition.

Que diriez-vous de ces exemples:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Maintenant, à ma connaissance, les trois premiers cas font tous la même chose: Test n'est pas un int, mais un pointeur vers un.

Le deuxième ensemble d'exemples est un peu plus délicat. Dans le cas 4, test et test2 seront tous deux des pointeurs vers un int, tandis que dans le cas 5, seul test est un pointeur, tandis que test2 est un int "réel". Et le cas 6? Identique au cas 5?

Michael Stum
la source
10
En C / C ++, les espaces blancs ne changent pas de sens.
Sulthan
19
7. int*test;?
Jin Kwon
3
+1 parce que je n'avais pensé à poser que 1 - 3. La lecture de cette question m'a appris quelque chose sur 4 - 6 auquel je n'avais jamais pensé.
vastlysuperiorman
@Sulthan C'est vrai 99% du temps, mais pas toujours. Au sommet de ma tête, il y avait le type de type basé sur un modèle dans l'exigence d'espace de type modèle (pré C ++ 11). Dans Foo<Bar<char>>le >>devait être écrit > >pour ne pas être traité comme un décalage à droite.
AnorZaken le
3
@AnorZaken Vous avez raison, c'est un commentaire assez ancien. Il existe plusieurs situations où un espace changera de signification, par exemple, l' ++opérateur d' incrémentation ne peut pas être divisé par un espace, les identificateurs ne peuvent pas être divisés par un espace (et le résultat peut être encore légal pour le compilateur mais avec un comportement d'exécution non défini). Les situations exactes sont très difficiles à définir compte tenu du désordre de syntaxe qu'est C / C ++.
Sulthan

Réponses:

129

4, 5 et 6 sont la même chose, seul le test est un pointeur. Si vous voulez deux pointeurs, vous devez utiliser:

int *test, *test2;

Ou, encore mieux (pour que tout soit clair):

int* test;
int* test2;
Milan Babuškov
la source
3
Alors le cas 4 est en fait un piège mortel? Y a-t-il une spécification ou une lecture supplémentaire qui explique pourquoi int * test, test2 ne fait que de la première variable un pointeur?
Michael Stum
8
@ Michael Stum C'est C ++ alors pensez-vous vraiment qu'il y a une explication logique?
Joe Phillips
6
Lisez K&R (le langage de programmation C). Cela explique tout cela très clairement.
Ferruccio
8
Les cas 4, 5 et 6 sont des «pièges mortels». C'est une des raisons pour lesquelles de nombreux gudes de style C / C ++ suggèrent une seule déclaration par instruction.
Michael Burr
14
L'espace blanc est insignifiant pour un compilateur C (ignorant le préprocesseur). Ainsi, peu importe le nombre d'espaces qu'il y a ou non entre l'astérisque et son environnement, il a exactement la même signification.
éphémère
45

Les espaces blancs autour des astérisques n'ont aucune signification. Les trois signifient la même chose:

int* test;
int *test;
int * test;

Le " int *var1, var2" est une mauvaise syntaxe qui est juste destinée à semer la confusion et doit être évitée. Il s'étend à:

int *var1;
int var2;
Ates Goral
la source
16
N'oublie pas int*test.
Mateen Ulhaq
1
l'espace avant ou après l'astérisque n'est qu'une question d'esthétique. Cependant, la norme de codage Google va de pair avec int *test( google-styleguide.googlecode.com/svn/trunk/… ). Soyez cohérent
@SebastianRaschka Le guide de style Google C ++ autorise explicitement le placement des astérisques. Cela a peut-être changé depuis que vous l'avez lu.
Jared Beck
33

De nombreuses directives de codage recommandent de ne déclarer qu'une seule variable par ligne . Cela évite toute confusion comme celle que vous aviez avant de poser cette question. La plupart des programmeurs C ++ avec lesquels j'ai travaillé semblent s'en tenir à cela.


Un petit aparté, je sais, mais quelque chose que j'ai trouvé utile est de lire les déclarations à l'envers.

int* test;   // test is a pointer to an int

Cela commence à très bien fonctionner, en particulier lorsque vous commencez à déclarer des pointeurs const et qu'il devient difficile de savoir si c'est le pointeur qui est const, ou si c'est la chose sur laquelle le pointeur pointe est const.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const
Scott Langham
la source
Bien que "une variable par ligne" semble utile, nous n'avons pas encore complètement résolu la situation où l'astérisque est plus à gauche, ou plus à droite. Je suis tout à fait sûr que dans le code à l'état sauvage, une variante prévaut; un peu comme certains pays conduisent du bon côté, et les autres dans la mauvaise direction, comme le Royaume-Uni. ;-)
shevy
Malheureusement, de mes aventures dans la nature, je vois beaucoup des deux styles. Dans mon équipe, nous utilisons maintenant le format clang avec un style sur lequel nous nous sommes mis d'accord. Cela signifie au moins que tout le code produit par notre équipe a le même style pour l'emplacement des espaces.
Scott Langham
33

Utilisez la «règle de la spirale dans le sens des aiguilles d'une montre» pour aider à analyser les déclarations C / C ++;

Il y a trois étapes simples à suivre:

  1. En commençant par l'élément inconnu, déplacez-vous en spirale / sens horaire; lorsque vous rencontrez les éléments suivants, remplacez-les par les instructions anglaises correspondantes:

    [X]ou []: Taille X du tableau de ... ou Taille du tableau non définie de ...

    (type1, type2): fonction passant type1 et type2 renvoyant ...

    *: pointeur (s) vers ...

  2. Continuez à faire cela dans le sens en spirale / dans le sens des aiguilles d'une montre jusqu'à ce que tous les jetons soient couverts.
  3. Résolvez toujours tout ce qui est entre parenthèses en premier!

En outre, les déclarations doivent être dans des déclarations séparées lorsque cela est possible (ce qui est vrai la grande majorité des fois).

Michael Burr
la source
4
Cela semble intimidant et assez horrible, désolé de le dire.
Joe Phillips
7
c'est le cas, mais cela semble plutôt une bonne explication pour certaines des constructions les plus compliquées
Michael Stum
@ d03boy: Il n'y a aucun doute - les déclarations C / C ++ peuvent être un cauchemar.
Michael Burr
2
La "spirale" n'a aucun sens, encore moins le "sens horaire". Je préfère l'appeler la "règle droite-gauche", car la syntaxe ne vous fait pas paraître droite-bas-gauche-haut, seulement droite-gauche.
Ruslan
J'ai appris cela comme la règle «droite-gauche-droite». Les gens du C ++ aiment souvent prétendre que toutes les informations de type se trouvent à gauche, ce qui conduit au int* x;style plutôt qu'au int *x;style traditionnel . Bien sûr, l'espacement n'a pas d'importance pour le compilateur, mais il affecte les humains. Le refus de la syntaxe réelle conduit à des règles de style qui peuvent ennuyer et dérouter les lecteurs.
Adrian McCarthy
12

Comme d'autres l'ont mentionné, 4, 5 et 6 sont identiques. Souvent, les gens utilisent ces exemples pour faire valoir que le *appartient à la variable au lieu du type. Bien que ce soit une question de style, il y a un débat pour savoir si vous devriez penser et l'écrire de cette façon:

int* x; // "x is a pointer to int"

ou de cette façon:

int *x; // "*x is an int"

FWIW Je suis dans le premier camp, mais la raison pour laquelle d'autres font valoir la deuxième forme est qu'elle résout (principalement) ce problème particulier:

int* x,y; // "x is a pointer to int, y is an int"

ce qui est potentiellement trompeur; à la place vous écririez soit

int *x,y; // it's a little clearer what is going on here

ou si vous voulez vraiment deux pointeurs,

int *x, *y; // two pointers

Personnellement, je dis de le garder à une variable par ligne, alors peu importe le style que vous préférez.

huskerchad
la source
6
c'est faux, comment appelez-vous int *MyFunc(void)? une *MyFuncfonction renvoie- intt-elle un ? non. Évidemment, nous devrions écrire int* MyFunc(void), et dire MyFuncest une fonction retournant un int*. Donc, pour moi, c'est clair, les règles d'analyse grammaticale C et C ++ sont tout simplement fausses pour la déclaration de variable. ils auraient dû inclure la qualification de pointeur dans le cadre du type partagé pour toute la séquence de virgules.
v.oddou
1
Mais *MyFunc() c'est un int. Le problème avec la syntaxe C est de mélanger la syntaxe de préfixe et de suffixe - si seulement postfix était utilisé, il n'y aurait pas de confusion.
Antti Haapala
1
Le premier camp combat la syntaxe du langage, conduisant à des constructions déroutantes comme int const* x;, que je trouve aussi trompeuses a * x+b * y.
Adrian McCarthy
11
#include <type_traits>

std::add_pointer<int>::type test, test2;
fredoverflow
la source
#include <windows.h>LPINT test, test2;
Stefan Dragnev
5

En 4, 5 et 6, testest toujours un pointeur et test2non un pointeur. L'espace blanc n'est (presque) jamais significatif en C ++.

1800 INFORMATIONS
la source
3

À mon avis, la réponse est LES DEUX, selon la situation. Généralement, l'OMI, il est préférable de mettre l'astérisque à côté du nom du pointeur, plutôt que le type. Comparez par exemple:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Pourquoi le deuxième cas est-il incohérent? Parce que par exemple int x,y;déclare deux variables du même type mais le type n'est mentionné qu'une seule fois dans la déclaration. Cela crée un précédent et un comportement attendu. Et int* pointer1, pointer2;est incompatible avec cela car il se déclare pointer1comme un pointeur, mais pointer2est une variable entière. Clairement sujet aux erreurs et, par conséquent, doit être évité (en plaçant l'astérisque à côté du nom du pointeur, plutôt que du type).

Cependant , il existe certaines exceptions où vous ne pourrez peut-être pas placer l'astérisque à côté d'un nom d'objet (et où cela importe où vous le mettez) sans obtenir un résultat indésirable - par exemple:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Enfin, dans certains cas, il peut être plus clair de placer l'astérisque à côté du nom du type , par exemple:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight

deLock
la source
2

La logique en C est que vous déclarez les variables comme vous les utilisez. Par exemple

char *a[100];

dit que ce *a[42]sera un char. Et a[42]un pointeur char. Et ainsi aest un tableau de pointeurs char.

Ceci parce que les auteurs du compilateur d'origine voulaient utiliser le même analyseur pour les expressions et les déclarations. (Ce n'est pas une raison très raisonnable pour un choix de conception de langage)

Michel Billaud
la source
Pourtant, l'écriture en déduit char* a[100]; également que ce *a[42];sera un pointeur charet a[42];un caractère.
yyny
Eh bien, nous en déduisons tous les mêmes conclusions, seul l'ordre varie.
Michel Billaud
Citation: "dit que * un [42] sera un caractère. Et un [42] un pointeur de caractère". Êtes-vous sûr que ce n'est pas l'inverse?
deLock
Si vous préférez l'inverse, disons qu'il a[42]s'agit d'un charpointeur et d' *a[42]un caractère.
Michel Billaud
2

Je dirais que la convention initiale était de mettre l'étoile du côté du nom du pointeur (côté droit de la déclaration

Vous pouvez suivre les mêmes règles, mais ce n'est pas grave si vous mettez des étoiles du côté du type. N'oubliez pas que la cohérence est importante, donc toujours mais l'étoile du même côté quel que soit le côté que vous avez choisi.

TheDrev
la source
Eh bien, l'analyseur semble autoriser l'une ou l'autre variante, mais si Dennis et Linus disent que cela devrait être du bon côté, c'est assez convaincant. Mais encore, il nous manque une sorte de justification, et ensuite aussi l'explication pourquoi cela est fait. C'est un peu comme une situation d'onglet contre espace - sauf que celle-ci a été résolue, car les gens qui utilisent des espaces plutôt que des onglets gagnent plus d'argent, selon StackOverflow ... :-)
shevy
1

Le pointeur est un modificateur du type. Il est préférable de les lire de droite à gauche afin de mieux comprendre comment l'astérisque modifie le type. «int *» peut être lu comme un «pointeur vers int». Dans plusieurs déclarations, vous devez spécifier que chaque variable est un pointeur ou elle sera créée comme une variable standard.

1,2 et 3) Le test est de type (int *). L'espace n'a pas d'importance.

4,5 et 6) Le test est de type (int *). Test2 est de type int. Encore une fois, les espaces sont sans importance.

Ron Warholic
la source
-3

En règle générale, beaucoup de gens semblent saisir ces concepts par: En C ++, une grande partie de la signification sémantique est dérivée par la liaison à gauche de mots-clés ou d'identificateurs.

Prends pour exemple:

int const bla;

Le const s'applique au mot "int". La même chose est avec les astérisques des pointeurs, ils s'appliquent au mot-clé à gauche d'eux. Et le nom réel de la variable? Oui, c'est déclaré par ce qu'il en reste.

mstrobl
la source
1
Cela ne répond pas à la question. Pire encore, si nous essayons d'en déduire une réponse, cela implique que l'astérisque se lie au type à sa gauche, ce qui, comme tout le monde l'a dit, est faux. Il se lie au nom de variable unique à sa droite.
underscore_d