Quelle est la différence entre const int *, const int * const et int const *?

1357

J'ai toujours gâcher la façon d'utiliser const int*, const int * constet int const *correctement. Existe-t-il un ensemble de règles définissant ce que vous pouvez et ne pouvez pas faire?

Je veux connaître toutes les choses à faire et à ne pas faire en termes d'affectations, de passer aux fonctions, etc.

MD XF
la source
175
Vous pouvez utiliser la "règle horaire / spirale" pour déchiffrer la plupart des déclarations C et C ++.
James McNellis
52
cdecl.org est un excellent site Web qui traduit automatiquement les déclarations C pour vous.
Dave Gallagher
6
@Calmarius: commencez où le nom-type est / devrait être, déplacez-vous vers la droite quand vous le pouvez, vers la gauche quand vous le devez . int *(*)(char const * const). Commencer à droite de la parenthésée *alors nous devons aller à gauche: pointer. En dehors des parens, on peut se déplacer à droite: pointer to function of .... Ensuite , nous devons aller à gauche: pointer to function of ... that returns pointer to int. Répétez l' opération pour augmenter le paramètre ( ...): pointer to function of (constant pointer to constant char) that returns pointer to int. Quelle serait la déclaration équivalente sur une ligne dans un langage facile à lire comme Pascal?
Mark K Cowan
1
@MarkKCowan En Pascal, ce serait quelque chose comme function(x:^char):^int. Ces types de fonction impliquent un pointeur vers une fonction, donc pas besoin de la spécifier, et Pascal n'applique pas l'exactitude de const. Il peut être lu de gauche à droite.
Calmarius
5
La première chose à gauche du "const" est ce qui est constant. Si "const" est la chose la plus à gauche, alors la première chose à droite est ce qui est constant.
Cupcake

Réponses:

2209

Lisez-le à l'envers (comme conduit par Clockwise / Spiral Rule ):

  • int* - pointeur vers int
  • int const * - pointeur vers const int
  • int * const - pointeur const vers int
  • int const * const - pointeur const vers const int

Maintenant, le premier constpeut être de chaque côté du type:

  • const int * == int const *
  • const int * const == int const * const

Si vous voulez devenir vraiment fou, vous pouvez faire des choses comme ça:

  • int ** - pointeur vers pointeur vers int
  • int ** const - un pointeur const vers un pointeur vers un int
  • int * const * - un pointeur vers un pointeur const vers un int
  • int const ** - un pointeur vers un pointeur vers un const int
  • int * const * const - un pointeur const vers un pointeur const vers un int
  • ...

Et pour nous assurer que nous comprenons clairement la signification de const:

int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.



int *const bar = &c;  // constant pointer to int 
                      // note, you actually need to set the pointer 
                      // here because you can't change it later ;)

*bar = 16;            // the value of c can be changed through the pointer.    

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.           

fooest un pointeur variable vers un entier constant. Cela vous permet de modifier ce que vous pointez mais pas la valeur que vous pointez. Le plus souvent, cela se voit avec des chaînes de style C où vous avez un pointeur sur a const char. Vous pouvez modifier la chaîne vers laquelle vous pointez, mais vous ne pouvez pas modifier le contenu de ces chaînes. Ceci est important lorsque la chaîne elle-même se trouve dans le segment de données d'un programme et ne doit pas être modifiée.

barest un pointeur constant ou fixe vers une valeur qui peut être modifiée. C'est comme une référence sans le sucre syntaxique supplémentaire. En raison de ce fait, vous utilisez généralement une référence où vous utilisez un T* constpointeur, sauf si vous devez autoriser les NULLpointeurs.

Prix ​​Matt
la source
482
Je voudrais ajouter une règle de base qui peut vous aider à vous rappeler comment découvrir si «const» s'applique au pointeur ou aux données pointées: divisez l'instruction au signe astérix, puis, si le mot-clé const apparaît dans la partie gauche (comme dans 'const int * foo') - il appartient aux données pointées, s'il est dans la partie droite ('int * const bar') - il s'agit du pointeur.
Michael
14
@Michael: Bravo à Michael pour une règle aussi simple pour se souvenir / comprendre la règle const.
sivabudh
10
@Jeffrey: le lire à l'envers fonctionne bien tant qu'il n'y a pas de parenthèses. Alors, eh bien ... utilisez typedefs
Mooing Duck
12
+1, mais un meilleur résumé serait: lire les déclarations de pointeur à l'envers , cela signifie, près de la déclaration de @Michael: arrêter la lecture normale de gauche à droite au premier astérisque.
Wolf
3
@gedamial c'est le cas, cela fonctionne bien, mais vous devez l'affecter en même temps que vous le déclarez (car vous ne pouvez pas réaffecter un "pointeur const"). const int x = 0; const int *const px = &x; const int *const *const p = &px;fonctionne très bien.
RastaJedi
357

Pour ceux qui ne connaissent pas la règle horaire / spirale: commencez par le nom de la variable, déplacez-vous dans le sens horaire (dans ce cas, reculez) vers le pointeur ou le type suivant . Répétez jusqu'à la fin de l'expression.

Voici une démo:

pointeur vers int

pointeur const vers const int

pointeur vers const int

pointeur vers const int

pointeur const vers int

Shijing Lv
la source
8
@Jan le lien pour l'exemple complexe n'a pas d'autorisations. pouvez-vous l'afficher directement ici, ou supprimer les restrictions d'affichage?
R71
8
@Rog, il disposait de toutes les autorisations d'accès ouvert ... Je n'ai pas écrit l'article et je n'ai pas moi-même d'autorisations d'accès, malheureusement. Cependant, voici une version archivée de l'article qui fonctionne toujours: archive.is/SsfMX
Jan Rüegg
8
L'exemple complexe est toujours juste de droite à gauche, mais comprend la résolution des parenthèses comme on le ferait normalement. La spirale dans le sens des aiguilles d'une montre ne facilite pas cela.
Matthew Read
4
Exemple ultime: void (*signal(int, void (*fp)(int)))(int);de archive.is/SsfMX
naXa
3
Ne vous fiez pas à cette règle. Ce n'est pas universel. Il y a des cas où cela échoue.
haccks
150

Je pense que tout est déjà répondu ici, mais je veux juste ajouter que vous devez vous méfier de typedefs! Ce ne sont PAS seulement des remplacements de texte.

Par exemple:

typedef char *ASTRING;
const ASTRING astring;

Le type astringn'est char * constpas const char *. C'est une des raisons pour lesquelles j'ai toujours tendance à mettre constà droite du type, et jamais au début.

Kaz Dragon
la source
20
Et pour moi, c'est la raison de ne jamais taper de pointeurs. Je ne vois pas l'avantage dans des choses comme typedef int* PINT(je suppose que c'est quelque chose qui vient des pratiques en C et de nombreux développeurs ont continué à le faire). Très bien, j'ai remplacé cela *par un P, cela n'accélère pas la frappe, en plus d'introduire le problème que vous mentionnez.
Mephane
1
@Mephane - Je peux le voir. Cependant, il me semble un peu à l'envers d'éviter une fonctionnalité linguistique agréable afin de continuer à utiliser une règle syntaxique exceptionnelle (à propos du placement "const"), plutôt que d'éviter d'utiliser la règle syntaxique exceptionnelle afin que vous puissiez utiliser cette fonctionnalité linguistique en toute sécurité .
TED
6
@Mephane PINTest en effet une utilisation plutôt stupide d'un typedef, surtout parce qu'il me fait penser que le système stocke utilise de la bière pour la mémoire. Les typedef sont cependant très utiles pour gérer les pointeurs vers les fonctions.
ApproachingDarknessFish
5
@KazDragon MERCI! Sans elle, je l' aurais foiré avec tous ceux typedefed PVOID, LPTSTRtrucs dans Win32 api!
David Lee
2
@Mephane: J'ai dû utiliser pSomething plusieurs fois lors de l'utilisation de certaines macros héritées qui ont été écrites pour accepter un type, mais qui se sépareraient si le type n'était pas un identifiant alphanumérique unique. :)
Groo
56

Comme à peu près tout le monde l'a souligné:

Quelle est la différence entre const X* p, X* const pet const X* const p?

Vous devez lire les déclarations de pointeur de droite à gauche.

  • const X* p signifie "p pointe vers un X qui est const": l'objet X ne peut pas être modifié via p.

  • X* const p signifie "p est un pointeur const vers un X qui n'est pas const": vous ne pouvez pas changer le pointeur p lui-même, mais vous pouvez changer l'objet X via p.

  • const X* const p signifie "p est un pointeur const vers un X qui est const": vous ne pouvez pas changer le pointeur p lui-même, ni changer l'objet X via p.

luke
la source
3
N'oubliez pas que const X* p;== X const * p;comme dans"p points to an X that is const": the X object can't be changed via p.
Jesse Chisholm
explication simple et agréable!
Edison Lo
50
  1. Référence constante:

    Une référence à une variable (ici int), qui est constante. Nous transmettons la variable comme référence principalement, car les références sont plus petites que la valeur réelle, mais il y a un effet secondaire et c'est parce qu'elle ressemble à un alias de la variable réelle. Nous pouvons accidentellement modifier la variable principale grâce à notre accès complet à l'alias, nous le rendons donc constant pour éviter cet effet secondaire.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
  2. Pointeurs constants

    Une fois qu'un pointeur constant pointe vers une variable, il ne peut pointer vers aucune autre variable.

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
  3. Pointeur sur constant

    Un pointeur à travers lequel on ne peut pas changer la valeur d'une variable qu'il pointe est connu comme un pointeur sur constant.

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
  4. Pointeur constant vers une constante

    Un pointeur constant vers une constante est un pointeur qui ne peut ni modifier l'adresse vers laquelle il pointe ni modifier la valeur conservée à cette adresse.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
Behrooz Tabesh
la source
20

La règle générale est que le constmot - clé s'applique à ce qui le précède immédiatement. Exception, un départ consts'applique à ce qui suit.

  • const int*est le même que int const*et signifie "pointeur vers int constant" .
  • const int* constest le même que int const* constet signifie "pointeur constant vers int constant" .

Edit: Pour les choses à faire et à ne pas faire, si cette réponse ne suffit pas, pourriez-vous être plus précis sur ce que vous voulez?

AProgrammer
la source
19

Cette question montre précisément pourquoi j'aime faire les choses comme je l'ai mentionné dans ma question est const après type id acceptable?

En bref, je trouve que le moyen le plus simple de se souvenir de la règle est que le "const" va après la chose à laquelle il s'applique. Donc, dans votre question, "int const *" signifie que l'int est constant, tandis que "int * const" signifierait que le pointeur est constant.

Si quelqu'un décide de le mettre au premier plan (par exemple: "const int *"), à titre d'exception spéciale dans ce cas, il s'applique à la chose suivante.

Beaucoup de gens aiment utiliser cette exception spéciale parce qu'ils pensent que c'est plus joli. Je n'aime pas ça, car c'est une exception, et donc confond les choses.

TED
la source
2
Je suis déchiré sur cette question. Logiquement, cela a du sens. Cependant, la plupart des développeurs c ++ écriraient const T*et c'est devenu plus naturel. Combien de fois utilisez-vous de toute T* constfaçon, une référence fera généralement l'affaire. Je me suis mordu par tout cela une fois en voulant un boost::shared_ptr<const T>et j'ai plutôt écrit const boost::shared_ptr<T>. Même problème dans un contexte légèrement différent.
Matt Price
En fait, j'utilise des pointeurs constants plus souvent que j'utilise des constantes. De plus, vous devez réfléchir à la façon dont vous allez réagir en présence de pointeurs vers des pointeurs (etc.) Certes, ceux-ci sont plus rares, mais il serait bien de penser aux choses de manière à pouvoir gérer ces situations avec Applomb.
TED
1
Le seul autre avantage agréable de placer la const à droite du type est que maintenant tout à gauche de tout constest le type de ce qui est const, et tout à sa droite est ce qui est réellement const. Prenons int const * const * p;l'exemple. Non, je n'écris pas normalement comme ça, ce n'est qu'un exemple. Premièrement const: tapez int, et l'int entier const est le contenu du pointeur const qui est le contenu de p. Deuxième const: type est un pointeur sur constint, const oblect est le contenu dep
dgnuff
18

Utilisation simple de const .

L'utilisation la plus simple consiste à déclarer une constante nommée. Pour ce faire, on déclare une constante comme si c'était une variable mais on l'ajoute constavant. Il faut l'initialiser immédiatement dans le constructeur car, bien sûr, on ne peut pas définir la valeur plus tard car cela la modifierait. Par exemple:

const int Constant1=96; 

créera une constante entière, appelée sans imagination Constant1, avec la valeur 96.

De telles constantes sont utiles pour les paramètres qui sont utilisés dans le programme mais n'ont pas besoin d'être modifiées après la compilation du programme. Elle présente un avantage pour les programmeurs par rapport à la #definecommande du préprocesseur C dans la mesure où elle est comprise et utilisée par le compilateur lui-même, et pas seulement substituée dans le texte du programme par le préprocesseur avant d'atteindre le compilateur principal, les messages d'erreur sont donc beaucoup plus utiles.

Il fonctionne également avec des pointeurs mais il faut faire attention où constdéterminer si le pointeur ou vers quoi il pointe est constant ou les deux. Par exemple:

const int * Constant2 

déclare qu'il Constant2s'agit d'un pointeur variable vers un entier constant et:

int const * Constant2

est une syntaxe alternative qui fait de même, alors que

int * const Constant3

déclare qu'il Constant3s'agit d'un pointeur constant vers un entier variable et

int const * const Constant4

déclare que Constant4 s'agit d'un pointeur constant vers un entier constant. Fondamentalement, «const» s'applique à tout ce qui se trouve à sa gauche immédiate (sauf s'il n'y a rien dans ce cas, il s'applique à tout ce qui est à sa droite immédiate).

ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

ufukgun
la source
9

J'avais le même doute que vous jusqu'à ce que je tombe sur ce livre du C ++ Guru Scott Meyers. Reportez-vous au troisième élément de ce livre où il parle en détail de l'utilisation const.

Suivez simplement ce conseil

  1. Si le mot constapparaît à gauche de l'astérisque, ce qui est indiqué est constant
  2. Si le mot constapparaît à droite de l'astérisque, le pointeur lui-même est constant
  3. Si constapparaît des deux côtés, les deux sont constants
rgk
la source
7

C'est simple mais délicat. S'il vous plaît noter que nous pouvons échanger le constqualificatif avec tout type de données ( int, char,float , etc.).

Voyons les exemples ci-dessous.


const int *p==> *pest en lecture seule [ pest un pointeur sur un entier constant]

int const *p==> *pest en lecture seule [ pest un pointeur sur un entier constant]


int *p const==> Mauvaise déclaration. Le compilateur renvoie une erreur de syntaxe.

int *const p==> pest en lecture seule [ pest un pointeur constant vers un entier]. Comme le pointeur pici est en lecture seule, la déclaration et la définition doivent être au même endroit.


const int *p const ==> Mauvaise déclaration. Le compilateur renvoie une erreur de syntaxe.

const int const *p ==> *pest en lecture seule

const int *const p1 ==> *pet psont en lecture seule [ pest un pointeur constant vers un entier constant]. Comme le pointeur pici est en lecture seule, la déclaration et la définition doivent être au même endroit.


int const *p const ==> Mauvaise déclaration. Le compilateur renvoie une erreur de syntaxe.

int const int *p ==> Mauvaise déclaration. Le compilateur renvoie une erreur de syntaxe.

int const const *p ==> *pest en lecture seule et équivaut àint const *p

int const *const p ==> *pet psont en lecture seule [ pest un pointeur constant vers un entier constant]. Comme le pointeur pici est en lecture seule, la déclaration et la définition doivent être au même endroit.

Abhijit Sahu
la source
6

Il existe de nombreux autres points subtils entourant l'exactitude de const en C ++. Je suppose que la question ici a simplement porté sur C, mais je vais donner quelques exemples connexes puisque la balise est C ++:

  • Vous passez souvent de gros arguments comme des chaînes, TYPE const &ce qui empêche la modification ou la copie de l'objet. Exemple :

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    Mais TYPE & const n'a pas de sens car les références sont toujours const.

  • Vous devez toujours étiqueter les méthodes de classe qui ne modifient pas la classe comme const, sinon vous ne pouvez pas appeler la méthode à partir d'une TYPE const &référence. Exemple :

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • Il existe des situations courantes où la valeur de retour et la méthode doivent être const. Exemple :

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    En fait, les méthodes const ne doivent pas renvoyer les données de classe internes en tant que référence à non-const.

  • Par conséquent, il faut souvent créer à la fois une méthode const et une méthode non-const en utilisant une surcharge const. Par exemple, si vous définissez T const& operator[] (unsigned i) const;, vous voudrez probablement aussi la version non-const donnée par:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Afaik, il n'y a pas de fonctions const en C, les fonctions non membres ne peuvent pas elles-mêmes être const en C ++, les méthodes const peuvent avoir des effets secondaires et le compilateur ne peut pas utiliser les fonctions const pour éviter les appels de fonctions en double. En fait, même une simple int const &référence pourrait témoigner que la valeur à laquelle elle se réfère pourrait être modifiée ailleurs.

Jeff Burdges
la source
6

La syntaxe de déclaration C et C ++ a été décrite à plusieurs reprises comme une expérience ratée par les concepteurs originaux.

Au lieu de cela, nous allons nommer le « pointeur type Type»; Je l'appellerai Ptr_:

template< class Type >
using Ptr_ = Type*;

Ptr_<char>Est maintenant un pointeur vers char.

Ptr_<const char>est un pointeur vers const char.

Et const Ptr_<const char>est un constpointeur vers const char.

Là.

entrez la description de l'image ici

Santé et hth. - Alf
la source
3
avez-vous un devis pour la première phrase?
sp2danny
@ sp2danny: La recherche sur Google de «l'échec de l'expérience de la syntaxe C» ne crache qu'un certain nombre d'entretiens avec Bjarne Stroustrup où il exprime son opinion dans cette direction, par exemple «Je considère la syntaxe du déclarant C comme une expérience qui a échoué» dans l'entretien de Slashdot. Je n'ai donc aucune référence pour l'affirmation concernant les points de vue des concepteurs originaux de C.Je suppose que cela peut être trouvé par un effort de recherche suffisamment fort, ou peut-être réfuté simplement en leur demandant, mais je pense que c'est mieux comme c'est maintenant. avec cette partie de la réclamation, toujours indécise et probablement vraie :)
Acclamations et hth. - Alf
1
"La syntaxe des déclarations C et C ++ a été décrite à plusieurs reprises comme une expérience ratée par les concepteurs d'origine." faux pour C, veuillez modifier votre phrase sur C ou fournir des citations.
Stargateur
3
@Stargateur: Apparemment, vous avez lu les commentaires précédents et trouvé quelque chose que vous pourriez utiliser pour la pédanterie. Bonne chance dans ta vie. Quoi qu'il en soit, les anciens comme moi se souviennent de beaucoup de choses que nous ne pouvons pas prouver sans nous engager dans des recherches très chronophages. Vous pourriez simplement me croire sur parole.
Bravo et hth. - Alf
1
@Stargateur "Sethi (...) a observé que la plupart des déclarations et expressions imbriquées deviendraient plus simples si l'opérateur d'indirection avait été pris comme opérateur postfix au lieu de préfixe, mais il était alors trop tard pour changer." est de DMR. Bien sûr, DMR n'a pas inventé de mots clés const et volatiles, ils provenaient de C ++ / X3J11, comme en témoigne cette page.
Antti Haapala du
6

Pour moi, la position de constsavoir si elle apparaît à GAUCHE ou à DROITE ou à la fois à GAUCHE et à DROITE par rapport à la *m'aide à comprendre la signification réelle.

  1. Un constà gauche *indique que l'objet pointé par le pointeur est un constobjet.

  2. Un constà DROITE *indique que le pointeur est un constpointeur.

Le tableau suivant est extrait du lecteur de cours de laboratoire de programmation standard C ++ Stanford CS106L.

entrez la description de l'image ici

sri
la source
3

Cela concerne principalement la deuxième ligne: meilleures pratiques, affectations, paramètres de fonction, etc.

Pratique générale. Essayez de faire tout ce constque vous pouvez. Ou pour dire les choses autrement, faites tout constpour commencer, puis supprimez exactement l'ensemble minimal deconst s nécessaire pour permettre au programme de fonctionner. Ce sera une grande aide pour atteindre la const-correct, et aidera à éviter que des bogues subtils ne soient introduits lorsque les gens essaient de les affecter à des choses qu'ils ne sont pas censés modifier.

Évitez const_cast <> comme la peste. Il existe un ou deux cas d'utilisation légitimes, mais ils sont très rares. Si vous essayez de changer unconst objet, vous ferez beaucoup mieux pour trouver celui qui l'a déclaré constau premier rythme et en discuter avec eux pour parvenir à un consensus sur ce qui devrait arriver.

Ce qui mène très bien aux missions. Vous ne pouvez attribuer quelque chose que s'il n'est pas constant. Si vous souhaitez attribuer à quelque chose qui est const, voir ci-dessus. N'oubliez pas que dans les déclarations int const *foo;etint * const bar; différentes choses sont const- d'autres réponses ici ont admirablement couvert cette question, donc je ne vais pas y entrer.

Paramètres de fonction:

Passez par valeur: par exemple, void func(int param)vous ne vous souciez pas d'une manière ou d'une autre sur le site appelant. L'argument peut être avancé qu'il existe des cas d'utilisation pour déclarer la fonction commevoid func(int const param) mais cela n'a aucun effet sur l'appelant, uniquement sur la fonction elle-même, en ce que la valeur transmise ne peut pas être modifiée par la fonction pendant l'appel.

Passez par référence: par exemple void func(int &param), cela fait maintenant une différence. Comme il vient d'être déclaré, il funcest autorisé de changer param, et tout site appelant doit être prêt à faire face aux conséquences. Changer la déclaration pour void func(int const &param)changer le contrat et les garanties qui funcne peuvent plus changerparam , ce qui signifie que ce qui est transmis est ce qui en ressortira. Comme d'autres l'ont noté, cela est très utile pour passer à bon marché un grand objet que vous ne voulez pas changer. Passer une référence est beaucoup moins cher que de passer un gros objet en valeur.

Passer par pointeur: par exemple , void func(int *param)et void func(int const *param)Ces deux sont à peu près synonyme de leurs homologues de référence, avec la mise en garde que la fonction appelée doit maintenant vérifier à nullptrmoins que d'autres assure la garantie contractuelle funcqu'il ne recevra jamais nullptrdansparam .

Article d'opinion sur ce sujet. Il est extrêmement difficile de prouver l'exactitude dans un cas comme celui-ci, il est tout simplement trop facile de faire une erreur. Alors ne prenez pas de risques et vérifiez toujours les paramètres du pointeur nullptr. Vous vous épargnerez la douleur et la souffrance et vous aurez du mal à trouver des bogues à long terme. Et en ce qui concerne le coût de la vérification, c'est très bon marché, et dans les cas où l'analyse statique intégrée au compilateur peut le gérer, l'optimiseur l'élidera de toute façon. Activez Link Time Code Generation pour MSVC, ou WOPR (je pense) pour GCC, et vous obtiendrez tout le programme, c'est-à-dire même dans les appels de fonction qui traversent une limite de module de code source.

À la fin de la journée, tout ce qui précède constitue un cas très solide pour toujours préférer les références aux pointeurs. Ils sont tout simplement plus sûrs.

dgnuff
la source
3

La constante avec l'int de chaque côté fera pointer vers la constante int :

const int *ptr=&i;

ou:

int const *ptr=&i;

constafter *fera un pointeur constant vers int :

int *const ptr=&i;

Dans ce cas, tous ces éléments sont un pointeur vers un entier constant , mais aucun d'entre eux n'est un pointeur constant:

 const int *ptr1=&i, *ptr2=&j;

Dans ce cas, tous sont pointeur sur un entier constant et ptr2 est pointeur constant sur un entier constant . Mais ptr1 n'est pas un pointeur constant:

int const *ptr1=&i, *const ptr2=&j;
chasseur
la source
3
  • si constest à gauche de *, il fait référence à la valeur (peu importe si c'est const intou int const)
  • si constest à droite de *, il fait référence au pointeur lui-même
  • ça peut être les deux en même temps

Un point important: const int *p ne signifie pas que la valeur à laquelle vous faites référence est constante !! . Cela signifie que vous ne pouvez pas le modifier via ce pointeur (ce qui signifie que vous ne pouvez pas affecter $ * p = ... `). La valeur elle-même peut être modifiée par d'autres moyens. Par exemple

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

Ceci est destiné à être utilisé principalement dans les signatures de fonction, pour garantir que la fonction ne peut pas modifier accidentellement les arguments passés.

note bleue
la source
2

Juste par souci d'exhaustivité pour C en suivant les autres explications, pas sûr pour C ++.

  • pp - pointeur vers pointeur
  • p - pointeur
  • données - la chose pointée, dans des exemples x
  • bold - variable en lecture seule

Aiguille

  • p données - int *p;
  • p données -int const *p;
  • p données -int * const p;
  • p données -int const * const p;

Pointeur vers pointeur

  1. pp p données - int **pp;
  2. pp p données -int ** const pp;
  3. pp p données -int * const *pp;
  4. pp p données -int const **pp;
  5. pp p données -int * const * const pp;
  6. pp p données -int const ** const pp;
  7. pp p données -int const * const *pp;
  8. pp p données -int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

N-niveaux de déférence

Continuez, mais que l'humanité vous excommunie.

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);
Comportement indéfini
la source
0
  1. const int*- pointeur vers un intobjet constant .

Vous pouvez modifier la valeur du pointeur; vous ne pouvez pas modifier la valeur de l' intobjet, le pointeur pointe vers.


  1. const int * const- pointeur constant vers un intobjet constant .

Vous ne pouvez pas modifier la valeur du pointeur ni la valeur de l' intobjet vers lequel pointe le pointeur.


  1. int const *- pointeur vers un intobjet constant .

Cette instruction équivaut à 1. const int*- Vous pouvez modifier la valeur du pointeur mais vous ne pouvez pas modifier la valeur de l' intobjet, le pointeur pointe vers.


En fait, il y a une 4ème option:

  1. int * const- pointeur constant vers l' intobjet.

Vous pouvez modifier la valeur de l'objet vers lequel pointe le pointeur, mais vous ne pouvez pas modifier la valeur du pointeur lui-même. Le pointeur pointera toujours vers le même intobjet mais cette valeur de cet intobjet peut être modifiée.


Si vous voulez déterminer un certain type de construction C ou C ++, vous pouvez utiliser la règle Clockwise / Spiral Rule faite par David Anderson; mais ne pas confondre avec la règle d'Anderson faite par Ross J. Anderson, qui est quelque chose de tout à fait distinct.

RobertS soutient Monica Cellio
la source