Existe-t-il une convention pour les déclarations de pointeur en C?

Lors de la déclaration de pointeurs en C, il existe 2 variantes (modifier: 3):

Variante A:
int* ptr;

Variante B:
int *ptr;

Variante C:
int * ptr;

  • Dans A, l’opérateur d’indirection a été ajouté au type.
  • En B, l’opérateur d’indirection a été ajouté à la variable.
  • En C, l’opérateur d’indirection se situe librement entre le type et la variable.

La façon dont un pointeur est déclaré diffère selon le type de documentation que j’ai lu. Certains auteurs semblent avoir une préférence pour certaines variantes, d’autres en utilisent plusieurs.

  • Ai-je raison de supposer qu’il n’y a pas de différence de fonctionnalité entre les différentes variantes?
  • Si oui, existe-t-il une convention pour quelle variante utiliser en C?

Il n’y a absolument aucune différence de fonctionnalité entre

 int* ptr; 

et

 int *ptr; 

Le choix que vous utilisez dépend de plusieurs styles de codage en conflit.

Quelque chose que personne d’autre n’a mentionné, c’est que

 int *ptr; 

correspond plus étroitement à la grammaire linguistique.

  • int *ptr; est une déclaration qui consiste en:
    • une déclaration-specifier int , suivie de
    • un déclarant , *ptr .

(Cela saute un certain nombre d’étapes, mais donne l’idée de base.)

Puisque déclaration suit use, cela signifie que *ptr est de type int . Il s’ensuit que ptr est de type int* .

On pourrait dire que cela le rend meilleur que

 int* ptr; 

pour la même raison que

 x = y+z; 

est mieux que

 x=y + z; 

Bien sûr, vous pouvez écrire

 int* ptr; 

et le lire comme ” ptr est du type int* “. Et beaucoup de programmeurs font exactement cela et s’entendent très bien (c’est généralement le style préféré en C ++). Le compilateur ne se soucie pas de la manière dont vous le faites, et quiconque lit votre code ne devrait pas avoir de difficulté à le comprendre de toute façon.

Mais quel que soit l’espacement que vous choisissez, vous devez comprendre quel int *ptr; signifie vraiment, de sorte que lorsque vous voyez

 int *ptr, i; 

dans le code de quelqu’un d’autre (comme vous le ferez inévitablement), vous comprendrez immédiatement que ptr est un pointeur et que i est un int.

Et si vous travaillez avec d’autres programmeurs sur un projet, vous devez respecter les conventions existantes dans les normes de codage ou, s’il n’y en a pas, la manière dont le code est déjà écrit. int *ptr; je préfère int *ptr; to int* ptr; , mais utiliser un mélange des deux styles est bien pire que d’utiliser systématiquement l’un ou l’autre.

Cela n’a d’importance que lorsque vous envisagez de déclarer plusieurs variables du même type sur la même ligne. Par exemple, si vous souhaitez plusieurs pointeurs int, procédez comme suit:

 int *a, *b, *c; 

Sur le plan stylistique, toutefois, cela est source de confusion lorsque vous déclarez une seule variable. Beaucoup de gens aiment voir le type suivi du nom de la variable, et le type est supposé être un pointeur sur int, pas sur int, alors ils préfèrent:

 int* a; int* b; int* c; 

C’est finalement à vous de décider si vous préférez un formulaire plutôt qu’un autre. En 20 ans de programmation professionnelle C, j’ai vu environ 50% des gens choisir l’un par rapport à l’autre.

Les deux signifient la même chose que d’autres ont dit. Un piège vous attend cependant. Considérons ce code:

 int* a, b; 

Vous pourriez penser que cela a déclaré des pointeurs sur int . Non donc mais sinon. En fait, a est int* mais b est int . C’est l’une des raisons pour lesquelles de nombreux programmeurs C ont préféré placer le * côté de la variable plutôt que le type. Quand écrit comme ça:

 int *a, b; 

vous êtes moins susceptible d’être induit en erreur quant à ce a et b .

Cela dit, de nombreuses normes de codage insistent pour que vous ne déclariez pas plus d’une variable par ligne, i, e.

 int* a; int b; 

Si vous suivez la règle de la variable par ligne, il n’ya aucune possibilité de confusion.

 T *a; 

est le moyen préféré du style C pour déclarer un pointeur sur T tel qu’il est utilisé dans le livre de Kernighan & Ritchie sur C.

 T* a; 

est la méthode de style C ++ préférée pour déclarer un pointeur sur T tel qu’utilisé dans le livre de Stroustrup sur le C ++.

Les deux notations sont équivalentes.

Les déclarations C sont basées sur les types d’ expressions et non sur les objects.

Si vous avez un pointeur sur un int nommé pi et que vous voulez accéder à la valeur entière sur laquelle il pointe, vous devez déréférencer le pointeur, comme dans:

 x = *pi; printf("%d", *pi); *pi = 1 + 2; 

etc. Le type de l’ expression *pi est int : la déclaration doit donc se lire comme suit:

 int *pi; 

Maintenant, supposons que vous ayez un tableau de pointeurs sur char ; pour accéder à n’importe quel caractère, vous devez d’abord entrer un indice dans le tableau, puis déréférencer le résultat:

 c = *pc[i]; if (*pc[j] == 'a') {...} 

etc. Encore une fois, le type de l’ expression *pc[i] est car, de sorte que la déclaration se lit comme suit:

 char *pc[N]; 

*pi et *pc[N] sont tous deux connus sous le nom de déclarateurs et spécifient des informations de type supplémentaires non fournies par le spécificateur de type. IOW, le type de tableau et le pointeur de pc sont spécifiés comme faisant partie du déclarant, tandis que char -ness est donné par le spécificateur de type.

Quant à savoir quel style est approprié

Ni l’un ni l’autre n’est “juste”, mais moi (et beaucoup d’autres programmeurs C) préfère écrire T *p par opposition à T* p , car il reflète plus fidèlement la grammaire linguistique (le * fait partie du déclarateur) et permet d’éviter confusion lors de la déclaration de plusieurs éléments. J’ai vu beaucoup trop d’exemples de personnes écrivant T* a, b; et s’attendre à ce que b soit un pointeur.

La réponse habituelle à cette critique est “ne déclarez pas plus d’un élément par ligne”. Ma réponse à cette réponse est “écris correctement tes déclarants et tu n’auras aucun problème”.

La plupart des programmeurs C ++ ont une école de pensée différente, qui préfèrent le style T* p , et je dois dire qu’il existe quelques cas (limités au C ++) où cela peut rendre le code plus lisible.

Cependant, cela ne fonctionne que pour de simples pointeurs vers T : le paradigme s’effondre rapidement lorsque vous commencez à traiter des tableaux de pointeurs, ou des pointeurs vers des tableaux, ou des pointeurs vers des fonctions, ou des pointeurs vers des tableaux de pointeurs vers des fonctions, etc. quelque chose comme

 T* (*(*p)[N])(); // p is a pointer to an array of pointers to functions // returning pointers to T. 

indique simplement une pensée confuse. Cependant, si vous sentez vraiment que vous devez suivre le paradigme T* p , vous pouvez toujours créer une série de typedefs:

MODIFIER

Essayons cela à nouveau:

 typedef T* TPtr; // pointer to T typedef TPtr TPtrFunc(); // function returning pointer to T typedef TPtrFunc* TPtrFuncPtr; // pointer to function returning // pointer to T typedef TPtrFuncPtr TPtrFuncPtrArray[N]; // N-element array of pointer to function // returning pointer to T TPtrFuncPtrArray* p; 

Pour l’amour de Dieu, ne fais pas ça.

En C, l’espace n’est pas important sauf là où il est nécessaire de séparer les jetons. Vos deux variantes sont syntaxiquement correctes.

La variante 1 associe l’opérateur de pointeur au type, ce qui est suffisamment logique. Pour moi, cela serait suffisant s’il n’y avait pas de raison pour que la variante 2 ait un sens.

La variante 2 correspond à la manière dont les déclarations C sont structurées. Dans la grammaire C, l’opérateur de pointeur appartient au déclarateur (c’est-à-dire avec le nom), pas au type. Ceci est important lors de la déclaration de plusieurs variables dans la même déclaration. Cela compte également dans un certain nombre de cas plus ésotériques.

Ainsi, pour moi, la variante 2 est plus compatible avec C. Mais l’une ou l’autre variante est défendable, et les deux variantes sont classiques.

Vous avez raison, les deux signifient exactement la même chose pour le compilateur. Les deux instructions produiront une variable de type (int *) .

Quant à ce qui est correct: boîte de Pandore! C’est un sujet de débat en général. Si vous travaillez pour une entreprise ou sur un logiciel libre, il est probablement préférable de vous en tenir à un style de codage défini. S’il n’y en a pas, j’utilise généralement le style de correspondance LNT (ne laisser aucune trace), quel que soit le style utilisé à l’évidence dans cette partie de la base de code.

On peut soutenir que pour le lecteur, il est plus facile à comprendre. Par exemple, int* ptr; met le * plus près de l’ int qui communique plus clairement avec le type (int *)