'const int' vs 'int const' comme paramètres de fonction en C ++ et C

116

Considérer:

int testfunc1 (const int a)
{
  return a;
}

int testfunc2 (int const a)
{
  return a;
}

Ces deux fonctions sont-elles les mêmes dans tous les aspects ou y a-t-il une différence?

Je suis intéressé par une réponse pour le langage C, mais s'il y a quelque chose d'intéressant dans le langage C ++, j'aimerais aussi savoir.

Nils Pipenbrinck
la source
Existe-t-il un mot clé const en C maintenant? Il n'y en avait pas, mais je ne connais pas très bien la norme C 99.
Onorio Catenacci
8
Vous n'avez pas besoin de l'être. C90 suffit. Ce n'était cependant pas dans le K&R C original.
Mark Baker
3
C'est un mot clé en C89 et ANSI. Je ne sais pas si c'était un mot-clé à l'époque de Kerningham et Richie.
Nils Pipenbrinck
7
Ce site traduit "C gibberish" en anglais cdecl.org
Motti
5
Je dirais "C charabia au charabia anglais", mais toujours sympa :)
Kos

Réponses:

175

const Tet T constsont identiques. Avec les types de pointeur, cela devient plus compliqué:

  1. const char* est un pointeur vers une constante char
  2. char const* est un pointeur vers une constante char
  3. char* const est un pointeur constant vers un (mutable) char

En d'autres termes, (1) et (2) sont identiques. La seule façon de faire le pointeur (plutôt que la pointee) constest d'utiliser un suffixe- const.

C'est pourquoi beaucoup de gens préfèrent toujours mettre constdu bon côté du type (style «East const»): cela rend son emplacement par rapport au type cohérent et facile à retenir (il semble aussi anecdotique de faciliter l'enseignement aux débutants ).

Konrad Rudolph
la source
2
C a const, étant donné: static const char foo [] = "foo"; vous feriez mieux de ne pas modifier foo.
James Antill
4
K&R C n'avait pas de const; C90 (et C99) le fait. C'est un peu limité par rapport au C ++, mais c'est utile.
Mark Baker
est-ce également vrai pour les références?
Ken du
1
@Ken Oui, c'est pareil.
Konrad Rudolph
1
@ étale-cohomology Bon point, ajouté. J'aurais dû être là depuis le début.
Konrad Rudolph
339

L'astuce consiste à lire la déclaration à l'envers (de droite à gauche):

const int a = 1; // read as "a is an integer which is constant"
int const a = 1; // read as "a is a constant integer"

Les deux sont la même chose. Par conséquent:

a = 2; // Can't do because a is constant

L'astuce de lecture à l'envers est particulièrement utile lorsque vous avez affaire à des déclarations plus complexes telles que:

const char *s;      // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a char"

*s = 'A'; // Can't do because the char is constant
s++;      // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++;      // Can't do because the pointer is constant
Ates Goral
la source
5
qu'en est-il de "char const * u"? Est-ce que cela se lit "Un pointeur vers un caractère constant" ou "un pointeur constant vers un caractère"? Cela semble ambigu. La norme dit le premier, mais pour le savoir, il faut tenir compte des règles de préséance et d'associativité.
Panayiotis Karabassis
5
@PanayiotisKarabassis Tout doit être traité comme une chaîne d'adjectifs, sans changer de place. char const *, lu de gauche à droite est: "pointer, const, char". C'est un pointeur vers const char. Lorsque vous dites "un pointeur constant", l'adjectif "constant" se trouve sur le pointeur. Donc, dans ce cas, votre liste d'adjectifs aurait dû être: "const, pointer, char". Mais vous avez raison, il y a une ambiguïté dans cette astuce. C'est vraiment un "truc", plus qu'une "règle" définitive.
Ates Goral
5
Lorsque vous déclarez une combinaison sauvage de tableau, de fonction, de pointeur et de pointeur de fonction, la lecture arrière ne fonctionne plus (malheureusement). Cependant, vous pouvez lire ces déclarations désordonnées dans un modèle en spirale . D'autres étaient tellement frustrés par eux qu'ils ont inventé le Go.
Martin JH
@Martin JH: Ne pourraient-ils pas être décomposés au moyen de typedefs? Et / ou utiliser des références pour éliminer les indirections?
Peter Mortensen
14

Il n'y a pas de différence. Ils déclarent tous les deux que "a" est un entier qui ne peut pas être modifié.

L'endroit où les différences commencent à apparaître est lorsque vous utilisez des pointeurs.

Ces deux:

const int *a
int const *a

déclarez "a" comme étant un pointeur vers un entier qui ne change pas. "a" peut être attribué à, mais "* a" ne le peut pas.

int * const a

déclare "a" comme étant un pointeur constant vers un entier. "* a" peut être attribué à, mais "a" ne le peut pas.

const int * const a

déclare "a" comme étant un pointeur constant vers un entier constant. Ni "a" ni "* a" ne peuvent être attribués.

static int one = 1;

int testfunc3 (const int *a)
{
  *a = 1; /* Error */
  a = &one;
  return *a;
}

int testfunc4 (int * const a)
{
  *a = 1;
  a = &one; /* Error */
  return *a;
}

int testfunc5 (const int * const a)
{
  *a = 1;   /* Error */
  a = &one; /* Error */
  return *a;
}
Andru Luvisi
la source
Le dernier exemple est l'explication la plus simple, super!
exru le
7

Prakash a raison de dire que les déclarations sont les mêmes, même si un peu plus d'explications sur la casse du pointeur pourraient être nécessaires.

"const int * p" est un pointeur vers un int qui ne permet pas de modifier l'int à travers ce pointeur. "int * const p" est un pointeur vers un int qui ne peut pas être modifié pour pointer vers un autre int.

Voir https://isocpp.org/wiki/faq/const-correctness#const-ptr-vs-ptr-const .

Fred Larson
la source
L'ancre ("faq-18.5.) Est cassée. À laquelle doit-elle se référer (il y en a plusieurs avec" const "et" * ")?
Peter Mortensen
@PeterMortensen: Ouais, lien pourriture. Merci. J'ai mis à jour le lien.
Fred Larson
5

const intest identique à int const, comme c'est le cas pour tous les types scalaires en C. En général, déclarer un paramètre de fonction scalaire comme cela constn'est pas nécessaire, car la sémantique appel par valeur de C signifie que toute modification de la variable est locale à sa fonction englobante.

Emerick Rogul
la source
4

Ce n'est pas une réponse directe mais une astuce connexe. Pour garder les choses droites, j'utilise toujours la convection "mis constà l'extérieur", où par "extérieur" j'entends l'extrême gauche ou l'extrême droite. De cette façon, il n'y a pas de confusion - le const s'applique à la chose la plus proche (le type ou le *). Par exemple,



int * const foo = ...; // Pointer cannot change, pointed to value can change
const int * bar = ...; // Pointer can change, pointed to value cannot change
int * baz = ...; // Pointer can change, pointed to value can change
const int * const qux = ...; // Pointer cannot change, pointed to value cannot change
Pat Notz
la source
6
Il vaut peut-être mieux utiliser la règle "const fait const ce qu'il en reste". Par exemple, "int * const foo" rend le pointeur "const", car le pointeur lui est laissé. Cependant, vous écririez la deuxième ligne "int const * bar", rend le int const, car il est laissé à lui. "int const * const * qux", fait les deux, l'int et le pointeur const, car l'un ou l'autre lui est laissé une fois.
Mecki
4

Ce sont les mêmes, mais en C ++, il y a une bonne raison de toujours utiliser const sur la droite. Vous serez cohérent partout car les fonctions membres const doivent être déclarées de cette façon:

int getInt() const;

Il change le thispointeur de la fonction de Foo * constà Foo const * const. Vois ici.

Nick Westgate
la source
3
C'est un type de const complètement différent.
Justin Meiners
1
Pourquoi est-ce complètement différent? Assez différent pour gagner un vote défavorable.
Nick Westgate
1
Oui, la question concerne la différence entre "const int" et "int const", votre réponse n'a rien à voir avec cela.
Justin Meiners
1
J'ai dit que ce sont les mêmes. Et pourtant, les réponses acceptées et les mieux votées donnent également des informations supplémentaires sur les types de pointeurs. Avez-vous également voté contre ceux-ci?
Nick Westgate
3

Oui, ils sont les mêmes pour juste int

et différent pour int*

prakash
la source
5
(const int *) et (int const *) sont identiques, ils sont juste différents de (int * const).
James Antill
3

Je pense que dans ce cas, ce sont les mêmes, mais voici un exemple où l'ordre compte:

const int* cantChangeTheData;
int* const cantChangeTheAddress;
user7545
la source
2
En effet, mais int const * est le même que le premier, donc l'ordre des int et const n'a pas d'importance, c'est juste l'ordre du * et du const qui fait.
Mark Baker