J'ai toujours gâcher la façon d'utiliser const int*
, const int * const
et 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.
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?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.Réponses:
Lisez-le à l'envers (comme conduit par Clockwise / Spiral Rule ):
int*
- pointeur vers intint const *
- pointeur vers const intint * const
- pointeur const vers intint const * const
- pointeur const vers const intMaintenant, le premier
const
peut ê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 intint ** const
- un pointeur const vers un pointeur vers un intint * const *
- un pointeur vers un pointeur const vers un intint const **
- un pointeur vers un pointeur vers un const intint * const * const
- un pointeur const vers un pointeur const vers un intEt pour nous assurer que nous comprenons clairement la signification de
const
:foo
est 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 aconst 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.bar
est 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 unT* const
pointeur, sauf si vous devez autoriser lesNULL
pointeurs.la source
const int x = 0; const int *const px = &x; const int *const *const p = &px;
fonctionne très bien.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:
la source
void (*signal(int, void (*fp)(int)))(int);
de archive.is/SsfMXJe pense que tout est déjà répondu ici, mais je veux juste ajouter que vous devez vous méfier de
typedef
s! Ce ne sont PAS seulement des remplacements de texte.Par exemple:
Le type
astring
n'estchar * const
pasconst char *
. C'est une des raisons pour lesquelles j'ai toujours tendance à mettreconst
à droite du type, et jamais au début.la source
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 unP
, cela n'accélère pas la frappe, en plus d'introduire le problème que vous mentionnez.PINT
est 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.PVOID
,LPTSTR
trucs dans Win32 api!Comme à peu près tout le monde l'a souligné:
Quelle est la différence entre
const X* p
,X* const p
etconst X* const p
?la source
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.
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.
Pointeurs constants
Une fois qu'un pointeur constant pointe vers une variable, il ne peut pointer vers aucune autre variable.
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.
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.
la source
La règle générale est que le
const
mot - clé s'applique à ce qui le précède immédiatement. Exception, un départconst
s'applique à ce qui suit.const int*
est le même queint const*
et signifie "pointeur vers int constant" .const int* const
est le même queint const* const
et 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?
la source
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.
la source
const T*
et c'est devenu plus naturel. Combien de fois utilisez-vous de touteT* const
façon, une référence fera généralement l'affaire. Je me suis mordu par tout cela une fois en voulant unboost::shared_ptr<const T>
et j'ai plutôt écritconst boost::shared_ptr<T>
. Même problème dans un contexte légèrement différent.const
est le type de ce qui est const, et tout à sa droite est ce qui est réellement const. Prenonsint const * const * p;
l'exemple. Non, je n'écris pas normalement comme ça, ce n'est qu'un exemple. Premièrementconst
: tapez int, et l'int entier const est le contenu du pointeur const qui est le contenu dep
. Deuxième const: type est un pointeur surconst
int, const oblect est le contenu dep
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
const
avant. 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: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
#define
commande 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ù
const
déterminer si le pointeur ou vers quoi il pointe est constant ou les deux. Par exemple:déclare qu'il
Constant2
s'agit d'un pointeur variable vers un entier constant et:est une syntaxe alternative qui fait de même, alors que
déclare qu'il
Constant3
s'agit d'un pointeur constant vers un entier variable etdé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
la source
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
const
apparaît à gauche de l'astérisque, ce qui est indiqué est constantconst
apparaît à droite de l'astérisque, le pointeur lui-même est constantconst
apparaît des deux côtés, les deux sont constantsla source
C'est simple mais délicat. S'il vous plaît noter que nous pouvons échanger le
const
qualificatif avec tout type de données (int
,char
,float
, etc.).Voyons les exemples ci-dessous.
const int *p
==>*p
est en lecture seule [p
est un pointeur sur un entier constant]int const *p
==>*p
est en lecture seule [p
est un pointeur sur un entier constant]int *p const
==> Mauvaise déclaration. Le compilateur renvoie une erreur de syntaxe.int *const p
==>p
est en lecture seule [p
est un pointeur constant vers un entier]. Comme le pointeurp
ici 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
==>*p
est en lecture seuleconst int *const p1
==>*p
etp
sont en lecture seule [p
est un pointeur constant vers un entier constant]. Comme le pointeurp
ici 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
==>*p
est en lecture seule et équivaut àint const *p
int const *const p
==>*p
etp
sont en lecture seule [p
est un pointeur constant vers un entier constant]. Comme le pointeurp
ici est en lecture seule, la déclaration et la définition doivent être au même endroit.la source
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'uneTYPE 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.la source
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'appelleraiPtr_
:Ptr_<char>
Est maintenant un pointeur verschar
.Ptr_<const char>
est un pointeur versconst char
.Et
const Ptr_<const char>
est unconst
pointeur versconst char
.Là.
la source
Pour moi, la position de
const
savoir si elle apparaît à GAUCHE ou à DROITE ou à la fois à GAUCHE et à DROITE par rapport à la*
m'aide à comprendre la signification réelle.Un
const
à gauche*
indique que l'objet pointé par le pointeur est unconst
objet.Un
const
à DROITE*
indique que le pointeur est unconst
pointeur.Le tableau suivant est extrait du lecteur de cours de laboratoire de programmation standard C ++ Stanford CS106L.
la source
Cela concerne principalement la deuxième ligne: meilleures pratiques, affectations, paramètres de fonction, etc.
Pratique générale. Essayez de faire tout ce
const
que vous pouvez. Ou pour dire les choses autrement, faites toutconst
pour 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 un
const
objet, vous ferez beaucoup mieux pour trouver celui qui l'a déclaréconst
au 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 sontconst
- 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 ¶m)
, cela fait maintenant une différence. Comme il vient d'être déclaré, ilfunc
est autorisé de changerparam
, et tout site appelant doit être prêt à faire face aux conséquences. Changer la déclaration pourvoid func(int const ¶m)
changer le contrat et les garanties quifunc
ne 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)
etvoid 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 ànullptr
moins que d'autres assure la garantie contractuellefunc
qu'il ne recevra jamaisnullptr
dansparam
.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.
la source
La constante avec l'int de chaque côté fera pointer vers la constante int :
ou:
const
after*
fera un pointeur constant vers int :Dans ce cas, tous ces éléments sont un pointeur vers un entier constant , mais aucun d'entre eux n'est un pointeur constant:
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:
la source
const
est à gauche de*
, il fait référence à la valeur (peu importe si c'estconst int
ouint const
)const
est à droite de*
, il fait référence au pointeur lui-mêmeUn 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 exempleCeci est destiné à être utilisé principalement dans les signatures de fonction, pour garantir que la fonction ne peut pas modifier accidentellement les arguments passés.
la source
Juste par souci d'exhaustivité pour C en suivant les autres explications, pas sûr pour C ++.
x
Aiguille
int *p;
int const *p;
int * const p;
int const * const p;
Pointeur vers pointeur
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N-niveaux de déférence
Continuez, mais que l'humanité vous excommunie.
la source
const int*
- pointeur vers unint
objet constant .Vous pouvez modifier la valeur du pointeur; vous ne pouvez pas modifier la valeur de l'
int
objet, le pointeur pointe vers.const int * const
- pointeur constant vers unint
objet constant .Vous ne pouvez pas modifier la valeur du pointeur ni la valeur de l'
int
objet vers lequel pointe le pointeur.int const *
- pointeur vers unint
objet constant .Cette instruction équivaut à 1.
const int*
- Vous pouvez modifier la valeur du pointeur mais vous ne pouvez pas modifier la valeur de l'int
objet, le pointeur pointe vers.En fait, il y a une 4ème option:
int * const
- pointeur constant vers l'int
objet.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
int
objet mais cette valeur de cetint
objet 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.
la source