Quelle est la différence entre const int*, int const * const, et l'int const *?
je gâche toujours comment utiliser const int*
, const int * const
, et int const *
correctement. Est-il un ensemble de règles définissant ce que vous pouvez faire et ne pas faire?
je veux savoir toutes les choses à faire et à ne pas faire en termes de missions, en passant à l'fonctions, etc.
14 réponses
lisez-le à l'envers (selon dans le sens des aiguilles D'une montre/règle en spirale ):
-
int*
- pointeur vers int -
int const *
- pointeur vers le bord -
int * const
- aiguille const à int -
int const * const
- pointeur de const à const int
maintenant le premier const
peut être de chaque côté du type ainsi:
-
const int *
==int const *
-
const int * const
==int const * const
Si vous voulez aller vraiment fou, vous pouvez faire des choses comme ceci:
-
int **
- pointeur vers pointeur vers int -
int ** const
- const pointeur vers un pointeur vers un int -
int * const *
- un pointeur vers un pointeur const int -
int const **
- un pointeur vers un pointeur vers une borne -
int * const * const
- const pointeur vers un pointeur const int - ...
et pour s'assurer que nous sommes clairs sur le sens de const
const int* foo;
int *const bar; //note, you actually need to set the pointer
//here because you can't change it later ;)
foo
est un pointeur variable vers un entier constant. Cela vous permet de changer de point, mais non la valeur que vous pointez. Le plus souvent, ce qui est vu avec C-style chaînes où vous avez un pointeur vers un const char
. Vous pouvez changer la chaîne vers laquelle vous pointez mais vous ne pouvez pas changer le contenu de ces chaînes. C'est important quand la chaîne elle-même est dans le segment de données d'un programme et ne devrait pas être changée.
bar
est un pointeur constant ou fixe vers une valeur qui peut être changée. C'est comme une référence, sans l'excès de sucre syntaxique. De ce fait, habituellement, vous devez utiliser une référence où vous utilisez un pointeur T* const
sauf si vous devez autoriser les pointeurs NULL
.
pour ceux qui ne connaissent pas la règle en spirale/dans le sens des aiguilles D'une montre: Commencez par le nom de la variable, déplacez clockwisely (dans ce cas, déplacez vers l'arrière) au prochain pointeur ou type . Répétez jusqu'à ce que l'expression se termine.
voici une démo:
je 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 que des remplacements de texte.
par exemple:
typedef char *ASTRING;
const ASTRING astring;
le type de astring
est char * const
, pas const char *
. C'est une des raisons pour lesquelles j'ai toujours tendance à mettre const
à la droite du type, et jamais au départ.
comme à peu près tout le monde l'a fait remarquer:
Quelle est la différence entre const X* p
, X* const p
et 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 const pointeur vers un X qui est const": vous ne pouvez pas changer le pointeur p lui-même, ni vous ne pouvez pas changer L'objet X via P.
-
référence constante:
une référence à une variable (ici int), qui est constante. Nous passons la variable comme référence principalement, parce que les références sont plus petites en taille que la valeur réelle, mais il y a un effet secondaire et c'est parce qu'il est comme un alias à la variable réelle. Nous pouvons accidentellement changer la variable principale par notre accès complet à l'alias, donc nous la rendons constante pour empêcher ce côté effet.
int var0 = 0; const int &ptr1 = var0; ptr1 = 8; // Error var0 = 6; // OK
-
Constant points
une fois qu'un pointeur constant pointe vers une variable, il ne peut pas pointer vers une autre variable.
int var1 = 1; int var2 = 0; int *const ptr2 = &var1; ptr2 = &var2; // Error
-
pointeur à constante
un pointeur par lequel on ne peut pas changer la valeur d'une variable qu'il pointe est connu comme un pointeur à constante.
int const * ptr3 = &var2; *ptr3 = 4; // Error
-
pointeur Constant vers une constante
Un pointeur constant sur une constante est un pointeur qui ne peut ni changer l'adresse de pointage et ne peut changer la valeur maintenue à cette adresse.
int var3 = 0; int var4 = 0; const int * const ptr4 = &var3; *ptr4 = 1; // Error ptr4 = &var4; // Error
cette question indique précisément pourquoi j'aime faire les choses de la façon dont je l'ai mentionné dans ma question est const après ID de type acceptable?
en bref, je trouve que la meilleure façon de se rappeler la règle est que la "const" va après la chose à laquelle elle s'applique. Donc, à votre question, "int const *" signifie que l'int est constante, tandis que "int * const" signifierait que le pointeur est constante.
Si quelqu'un décide de le mettre à l'avant (par exemple: "const int *"), à titre exceptionnel, dans ce cas, il s'applique à la chose après.
beaucoup de gens aiment utiliser cette exception spéciale parce qu'ils pensent qu'il semble plus agréable. Je n'aime pas, parce que c'est une exception, et donc confond les choses.
la règle générale est que le mot-clé const
s'applique à ce qui le précède immédiatement. Exception, un const
de départ s'applique à ce qui suit.
-
const int*
est la même queint const*
et signifie "pointeur vers constant int" . -
const int* const
est le même queint const* const
et signifie "pointeur constant vers constant int" .
Edit: Pour les choses à faire et à ne pas faire, si cette réponse n'est pas suffisante, pourriez-vous être plus précis sur ce que vous voulez?
Simple utilisation de "const’
l'utilisation La plus simple est de déclarer une constante nommée. Pour ce faire, on déclare une constante, comme si c'était une variable, mais ajouter "const" avant de. On doit réagir immédiatement dans le constructeur, parce que, évidemment, on ne peut pas définir la valeur plus tard, comme ce serait altérer. Par exemple,
const int Constant1=96;
va créer une constante entière, unimaginativement appelé ‘Constant1’, avec la valeur 96.
ces constantes sont utiles pour les paramètres qui sont utilisés dans le programme mais qui ne doivent pas être changés après que le programme est compilé. Il présente un avantage pour les programmeurs par rapport à la commande du préprocesseur C ‘#define’ en ce sens qu'il est compris et utilisé par le compilateur lui-même, et non simplement substitué dans le texte du programme par le préprocesseur avant d'atteindre le compilateur principal, de sorte que les messages d'erreur sont beaucoup plus utiles.
il fonctionne aussi avec des pointeurs, mais on doit être attention où "const" pour déterminer si le pointeur ou ce qu'il pointe est constant ou les deux. Par exemple,
const int * Constant2
déclare que Constant2 est variable pointeur vers une constante de type entier et
int const * Constant2
est une syntaxe alternative qui fait la même chose, alors que
int * const Constant3
déclare que Constant3 est constante pointeur vers une variable de type entier et
int const * const Constant4
déclare que Constant4 est pointeur constant vers un entier constant. Fondamentalement ' const’ s'applique à ce qui est sur sa gauche immédiate (sauf s'il n'y a rien là-bas, auquel cas il s'applique à ce qui est son droit immédiat).
réf: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
j'avais le même doute que vous jusqu'à ce que je tombe sur ce Livre par le gourou C++ Scott Meyers. Référez-vous au troisième point de ce livre où il parle en détail de l'utilisation de const
.
suivez ce conseil
- si le mot
const
apparaît à gauche de l'astérisque, ce qui est indiqué est constant - Si le mot
const
apparaît à droite de l'astérisque, le pointer lui-même est constant - si
const
apparaît des deux côtés, les deux sont constants
il y a beaucoup d'autres points subtils entourant l'exactitude de const en C++. Je suppose que la question ici a simplement été sur C, mais je vais donner quelques exemples liés puisque la balise est C++ :
-
vous passez souvent de grands arguments comme des chaînes de caractères comme
TYPE const &
qui empêche l'objet d'être modifié ou copié. Exemple:TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
mais
TYPE & const
est dénué de sens parce que 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 référenceTYPE const &
. Exemple:bool TYPE::operator==(const TYPE &rhs) const { ... }
-
il y a des situations courantes où la valeur de retour et la méthode doivent être considérées. Exemple:
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
en fait, les méthodes const ne doivent pas renvoyer de données internes de classe comme 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 la surcharge const. Par exemple, si vous définissez
T const& operator[] (unsigned i) const;
, alors 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 les fonctions de const en C, les fonctions non-membres ne peuvent pas elles-mêmes être const en C++, Les méthodes de const peuvent avoir des effets secondaires, et le compilateur ne peut pas utiliser les fonctions de const pour éviter les appels de fonction dupliqués. En fait, même une simple référence int const &
pourrait témoigner que la valeur à laquelle elle se réfère soit changée ailleurs.
c'est simple mais délicat. Veuillez noter que nous pouvons échanger le qualificatif const
avec n'importe quel 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 vers un entier constant]
int const *p
==> *p
est en lecture seule [ p
est un pointeur vers une constante entier]
int *p const
==> Fausse Déclaration . Compilateur lance une erreur de syntaxe.
int *const p
==> p
est en lecture seule [ p
est un pointeur constant vers un entier].
Comme le pointeur p
est ici en lecture seule, la déclaration et la définition doivent être au même endroit.
const int *p const
==> faux. déclaration. Compilateur lance une erreur de syntaxe.
const int const *p
==> *p
est en lecture seule
const int *const p1
==> *p
et p
sont en lecture seule [ p
est un pointeur constant vers un entier constant]. Comme le pointeur p
est ici en lecture seule, la déclaration et la définition doivent être au même endroit.
int const *p const
==> faux Déclaration. Compilateur lance une erreur de syntaxe.
int const int *p
==> Fausse Déclaration . Compilateur lance une erreur de syntaxe.
int const const *p
==> *p
est en lecture seule et équivaut à int const *p
int const *const p
==> *p
et p
sont en lecture seule [ p
est un pointeur constant vers un entier constant]. Comme pointeur p
ici est en lecture seule, la déclaration et la définition devrait être au même endroit.
la syntaxe de la déclaration C et C++ a été décrite à plusieurs reprises comme une expérience ratée, par les concepteurs originaux.
à la place, nom le type "pointeur vers Type
"; Je l'appellerai Ptr_
:
template< class Type >
using Ptr_ = Type*;
Maintenant Ptr_<char>
est un pointeur vers char
.
Ptr_<const char>
est un pointeur vers const char
.
et const Ptr_<const char>
est un const
pointeur vers const char
.
là.
la const avec l'int de chaque côté fera pointeur à Constant int .
const int *ptr=&i;
ou
int const *ptr=&i;
const après ' * ' constante pointeur sur int .
int *const ptr=&i;
dans ce cas , tous ceux-ci sont pointeur à l'entier constant , mais aucun de ceux-ci sont pointeur constant.
const int *ptr1=&i, *ptr2=&j;
dans ce tous les cas sont pointeur à l'entier constant et ptr2 est pointeur constant à l'entier constant . Mais ptr1 n'est pas un pointeur constant.
int const *ptr1=&i, *const ptr2=&j;
il s'agit principalement de la deuxième ligne: meilleures pratiques, affectations, paramètres de fonction, etc.
pratique Générale. Essayez de faire tout const
que vous pouvez. Ou en d'autres termes, tout faire const
pour commencer, puis retirez exactement l'ensemble minimal de const
s nécessaire pour permettre au programme de fonctionner. Cela sera d'une grande aide pour atteindre la const-rectitude, et aidera à s'assurer que les bogues subtils ne sont pas introduits quand les gens essaient d'affecter des choses qu'ils ne sont pas censés modifier.
Evitez const_cast < > comme la peste. Il y a un ou deux cas d'utilisation légitime, mais ils sont très rares. Si vous essayez de changer un objet const
, vous ferez beaucoup mieux de trouver celui qui l'a déclaré const
dans le premier pas et de discuter de la question avec eux pour parvenir à un consensus sur ce qui devrait se passer.
qui mène très bien dans les affectations. Vous pouvez affecter dans quelque chose que si elle est non-const. Si vous voulez affecter dans quelque chose qui est const, voir ci-dessus. Rappelez - vous que dans les déclarations int const *foo;
et int * const bar;
différentes choses sont const
- d'autres réponses ici ont couvert cette question admirablement, donc je ne vais pas entrer dans le sujet.
paramètres de fonction:
Passage par valeur: par exemple void func(int param)
vous n'avez pas de soins d'une façon ou de l'autre au site appelant. Le on peut faire valoir qu'il existe des cas d'utilisation pour déclarer la fonction comme void func(int const param)
mais que cela n'a aucun effet sur l'appelant, seulement sur la fonction elle-même, en ce que quelle que soit la valeur passée ne peut pas être changée par la fonction pendant l'appel.
Passage par référence: par exemple void func(int ¶m)
Maintenant, il y a une différence. Comme vient de le déclarer func
est autorisé à changer param
, et tout site appelant devrait être prêt à faire face aux conséquences. La modification de la déclaration à void func(int const ¶m)
change le contrat , et garantit que func
ne peut pas changer param
, ce qui signifie ce qui est passé dans ce qui sortira. Comme d'autres ont noté ceci 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 grand objet en valeur.
Passage par pointeur: par exemple void func(int *param)
et void func(int const *param)
Ces deux sont à peu près synonyme de leurs les contreparties de référence, avec la mise en garde que la fonction appelée doit maintenant vérifier pour nullptr
à moins qu'une autre garantie contractuelle assure func
qu'elle ne recevra jamais un nullptr
dans param
.
article d'Opinion sur le sujet. Prouver l'exactitude dans un cas comme celui-ci est extrêmement difficile, c'est juste trop facile de faire une erreur. Donc ne prenez pas de risques, et vérifiez toujours les paramètres de pointeur pour nullptr
. Vous économiserez de vous-même la douleur et la souffrance et difficile de trouver des insectes à long terme. Et comme pour le coût de la vérification, c'est vraiment pas cher, et dans les cas où l'analyse statique intégrée dans le compilateur peut le gérer, l'optimiseur va éluder toute façon. Mettez en marche la génération de code de temps de lien pour MSVC, ou WOPR (je pense) pour GCC, et vous l'obtiendrez le programme large, c.-à-d. 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 fait un cas très solide pour toujours préférer les références aux pointeurs. Ils sont seulement plus sûr de tous.