Je ne parle pas de pointeurs vers des valeurs const, mais de pointeurs const eux-mêmes.
J'apprends le C et le C ++ au-delà des choses très basiques et jusqu'à aujourd'hui, j'ai réalisé que les pointeurs sont passés par valeur aux fonctions, ce qui a du sens. Cela signifie qu'à l'intérieur d'une fonction, je peux faire pointer le pointeur copié vers une autre valeur sans affecter le pointeur d'origine de l'appelant.
Alors, quel est l'intérêt d'avoir un en-tête de fonction qui dit:
void foo(int* const ptr);
À l'intérieur d'une telle fonction, vous ne pouvez pas faire pointer ptr sur autre chose car c'est const et vous ne voulez pas qu'il soit modifié, mais une fonction comme celle-ci:
void foo(int* ptr);
Ça marche aussi bien! car le pointeur est copié de toute façon et le pointeur de l'appelant n'est pas affecté même si vous modifiez la copie. Alors, quel est l'avantage de const?
const
paramètre.const
garanties -correctness. Cela nous permet simplement de nous sentir plus sûrs que notre code est indubitablement correct.Réponses:
const
est un outil que vous devriez utiliser dans la poursuite d'un concept C ++ très important:Même si cela ne change pas la fonctionnalité, l'ajout
const
génère une erreur de compilation lorsque vous faites des choses que vous ne vouliez pas faire. Imaginez la faute de frappe suivante:Si vous utilisez
int* const
, cela générerait une erreur du compilateur car vous modifiez la valeur enptr
. L'ajout de restrictions via la syntaxe est une bonne chose en général. N'allez pas trop loin - l'exemple que vous avez donné est un cas où la plupart des gens ne prennent pas la peine de l'utiliserconst
.la source
Je tiens à n'utiliser que
const
arguments car cela permet plus de vérifications du compilateur: si je réassigne accidentellement une valeur d'argument à l'intérieur de la fonction, le compilateur me mord.Je réutilise rarement des variables, il est plus propre de créer de nouvelles variables pour contenir de nouvelles valeurs, donc essentiellement toutes mes déclarations de variables le sont
const
(sauf dans certains cas tels que les variables de boucle oùconst
cela empêcherait le code de fonctionner).Notez que cela n'a de sens que dans la définition d'une fonction. Il n'appartient pas à la déclaration , ce que voit l'utilisateur. Et l'utilisateur ne se soucie pas de savoir si j'utilise
const
pour les paramètres à l'intérieur de la fonction.Exemple:
Remarquez comment sont à la fois l'argument et la variable locale
const
. Ni l'un ni l'autre n'est nécessaire, mais avec des fonctions encore légèrement plus grandes, cela m'a évité à plusieurs reprises de faire des erreurs.la source
+1
d'un autreconst
fanatique obsédé. Cependant, je préfère que mes compilateurs m'aboient dessus. Je fais trop d'erreurs et j'en souffrirais gravement s'ils mordent.const
stratégie " sauf s'il y a une bonne raison". Il y a cependant quelques bonnes exceptions, par exemple Copie et échangeconst
l'argument de l'argument et, de préférence, commentez pourquoi. Ce petit peu de travail supplémentaire ne justifie pas de marquer tous les arguments comme nonconst
par défaut et de vous ouvrir à toutes les erreurs potentielles que cela crée.Votre question touche à quelque chose de plus général: les arguments de fonction doivent-ils être const?
La constance des arguments de valeur (comme votre pointeur) est un détail d'implémentation et ne fait pas partie de la déclaration de fonction. Cela signifie que votre fonction est toujours la suivante:
Il appartient entièrement à l' implémenteur de la fonction si elle souhaite utiliser la variable d'argument de la portée des fonctions de manière mutable ou constante:
Alors, suivez la règle simple de ne jamais mettre
const
dans la déclaration (en-tête), et mettez-la dans la définition (implémentation) si vous ne voulez ou n'avez pas besoin de modifier la variable.la source
const
, la déclaration et la (partie prototype de) la définition vont généralement être identiques.const
dans la déclaration. C'est entièrement à vous si vous souhaitez ajouter le qualificatif dans l'implémentation.const
la déclaration mais pas la définition a du sens. C'est juste un problème dans la langue que c'est le seul cas où rendre la déclaration et la définition non identiques a du sens. (Ce n'est pas le seul problème de C, bien sûr.)Le qualificatif const de niveau supérieur est ignoré dans les déclarations, de sorte que les déclarations de la question déclarent exactement la même fonction. D'autre part, dans la définition (implémentation), le compilateur vérifiera que si vous marquez le pointeur comme const, il n'est pas modifié à l'intérieur du corps de la fonction.
la source
int* t ptr
est une erreur de syntaxe. Sans cela, les deux sont identiques pour des raisons de surcharge.Vous avez raison, pour l'appelant, cela ne fait absolument aucune différence. Mais pour l'auteur de la fonction, cela peut être un filet de sécurité "d'accord, je dois m'assurer de ne pas faire ce point sur la mauvaise chose". Pas très utile mais pas inutile non plus.
C'est fondamentalement la même chose que d'avoir un
int const the_answer = 42
dans votre programme.la source
const int
etint const
sont équivalents, tandis queconst int*
etint* const
ont deux significations différentes!int const
partie; Je mets le type avant const (cela ne semble pas naturel) depuis un certain temps et je suis conscient des différences. Cet article pourrait s'avérer utile. J'ai moi-même eu des raisons légèrement différentes de passer à ce style cependant.Il y a beaucoup de
const
mots - clés, c'est plutôt complexe. En général, ajouter beaucoup de const à votre programme est considéré comme une bonne pratique de programmation, recherchez sur le Web "const correctness" et vous trouverez de nombreuses informations à ce sujet.Le mot clé const est un soi-disant "qualificatif de type", les autres sont
volatile
etrestrict
. Au moins volatile suit les mêmes règles (déroutantes) que const.Tout d'abord, le mot-clé const a deux objectifs. Le plus évident est de protéger les données (et les pointeurs) contre une utilisation abusive intentionnelle ou accidentelle en les rendant en lecture seule. Toute tentative de modification d'une variable const sera repérée par le compilateur au moment de la compilation.
Mais il y a aussi un autre but dans tout système avec une mémoire en lecture seule, à savoir de s'assurer qu'une certaine variable est allouée à l'intérieur d'une telle mémoire - cela pourrait être EEPROM ou flash par exemple. Celles-ci sont connues sous le nom de mémoires non volatiles, NVM. Une variable allouée dans NVM suivra bien entendu toutes les règles d'une variable const.
Il existe plusieurs façons d'utiliser le
const
mot - clé:Déclarez une variable constante.
Cela peut être fait soit comme
Ces deux formes sont tout à fait équivalentes . Ce dernier style est considéré comme un mauvais style et ne doit pas être utilisé.
La raison pour laquelle la deuxième ligne est considérée comme un mauvais style est probablement parce que les "spécificateurs de classe de stockage" tels que static et extern peuvent également être déclarés après le type réel,
int static
etc. Mais le faire pour les spécificateurs de classe de stockage est étiqueté comme une fonctionnalité obsolète par le comité C (ISO 9899 N1539 draft, 6.11.5). Par conséquent, pour des raisons de cohérence, il ne faut pas non plus écrire de qualificatifs de type de cette manière. Cela ne sert à rien d'autre que de confondre le lecteur de toute façon.Déclarez un pointeur vers une variable constante.
Cela signifie que le contenu de «X» ne peut pas être modifié. C'est la manière normale de déclarer des pointeurs comme celui-ci, principalement dans le cadre des paramètres de fonction pour la "correction de const". Parce que 'X' n'a pas besoin d'être déclaré comme const, il peut s'agir de n'importe quelle variable. En d'autres termes, vous pouvez toujours «mettre à niveau» une variable vers const. Techniquement, C permet également de passer de const à une simple variable par des typecasts explicites, mais cela est considéré comme une mauvaise programmation et les compilateurs donnent généralement des avertissements contre cela.
Déclarer un pointeur constant
Cela signifie que le pointeur lui - même est constant. Vous pouvez modifier ce sur quoi il pointe, mais vous ne pouvez pas modifier le pointeur lui-même. Cela n'a pas beaucoup d'utilisations, il y en a quelques-unes, comme s'assurer qu'un pointeur pointé sur (pointeur vers pointeur) ne voit pas son adresse modifiée lorsqu'il est passé en paramètre à une fonction. Vous devrez écrire quelque chose de pas trop lisible comme ceci:
Je doute que de nombreux programmeurs C puissent obtenir le const et * directement là-dedans. Je sais que je ne peux pas - j'ai dû vérifier avec GCC. Je pense que c'est pourquoi vous voyez rarement cette syntaxe pour le pointeur à pointeur, même si elle est considérée comme une bonne pratique de programmation.
Les pointeurs de constantes peuvent également être utilisés pour garantir que la variable de pointeur elle-même est déclarée dans la mémoire en lecture seule, par exemple, vous pouvez déclarer une sorte de table de recherche basée sur des pointeurs et l'allouer dans NVM.
Et bien sûr, comme indiqué par d'autres réponses, des pointeurs constants peuvent également être utilisés pour imposer la "correction de const".
Déclarer un pointeur constant vers des données constantes
Ce sont les deux types de pointeurs décrits ci-dessus combinés, avec tous les attributs des deux.
Déclarer une fonction membre en lecture seule (C ++)
Puisqu'il s'agit de C ++, je dois également mentionner que vous pouvez déclarer les fonctions membres d'une classe comme const. Cela signifie que la fonction n'est pas autorisée à modifier un autre membre de la classe lorsqu'elle est appelée, ce qui à la fois empêche le programmeur de la classe d'erreurs accidentelles mais informe également l'appelant de la fonction membre qu'il ne dérangera rien en l'appelant. La syntaxe est:
la source
(imo) cela n'a vraiment pas de sens par défaut. la valeur par défaut la plus judicieuse est de passer comme pointeur non réassignable (
int* const arg
). autrement dit, j'aurais préféré que les pointeurs passés comme arguments soient déclarés implicitement const.l'avantage est que c'est assez facile et parfois peu clair lorsque vous modifiez l'adresse vers laquelle pointe l'argument, de sorte que vous pouvez introduire un bogue quand il n'est pas const assez facilement. modifier l'adresse est atypique. il est plus clair de créer une variable locale si votre intention est de modifier l'adresse. De plus, la manipulation de pointeurs bruts est un moyen facile d'introduire des bogues.
il est donc plus clair de passer par une adresse immuable et de créer une copie (dans ces cas atypiques) lorsque vous souhaitez modifier l'adresse vers laquelle pointe l'argument:
ajoutant que local est pratiquement gratuit et réduit les risques d'erreurs, tout en améliorant la lisibilité.
à un niveau supérieur: si vous manipulez l'argument en tant que tableau, il est généralement plus clair et moins sujet aux erreurs pour le client de déclarer l'argument en tant que conteneur / collection.
en général, l'ajout de const aux valeurs, arguments et adresses est une bonne idée car vous ne réalisez pas toujours les effets secondaires, que le compilateur applique volontiers. par conséquent, il est aussi utile que const comme utilisé dans plusieurs autres cas (par exemple, la question est similaire à «Pourquoi devrais-je déclarer des valeurs const?»). heureusement, nous avons aussi des références, qui ne peuvent pas être réaffectées.
la source
const
mot - clé, il devrait avoir unmutable
mot - clé (enfin, il a, mais avec la mauvaise sémantique).Si vous effectuez une programmation de systèmes embarqués ou de pilotes de périphériques où vous avez des périphériques mappés en mémoire, les deux formes de `` const '' sont souvent utilisées, l'une pour empêcher le pointeur d'être réaffecté (car il pointe vers une adresse matérielle fixe.) Et, si le périphérique le registre vers lequel il pointe est un registre matériel en lecture seule, puis un autre const détectera beaucoup d'erreurs au moment de la compilation plutôt qu'à l'exécution.
Un registre de puce périphérique 16 bits en lecture seule peut ressembler à ceci:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Ensuite, vous pouvez facilement lire le registre du matériel sans avoir à recourir au langage d'assemblage:
input_word = *peripheral;
la source
int iVal = 10; int * const ipPtr = & iVal;
Tout comme une variable const normale, un pointeur const doit être initialisé à une valeur lors de la déclaration, et sa valeur ne peut pas être modifiée.
Cela signifie qu'un pointeur const pointera toujours sur la même valeur. Dans le cas ci-dessus, ipPtr pointera toujours vers l'adresse d'iVal. Cependant, comme la valeur pointée est toujours non-const, il est possible de modifier la valeur pointée via le déréférencement du pointeur:
* ipPtr = 6; // autorisé, puisque pnPtr pointe vers un int non-const
la source
La même question peut être posée pour tout autre type (pas seulement pour les pointeurs):
la source
Votre question porte davantage sur la raison pour laquelle définir une variable en tant que paramètre de pointeur const et pas seulement vers une fonction. Les mêmes règles s'appliquent ici que lorsque vous définissez une variable comme constante, s'il s'agit d'un paramètre de fonction ou de variable membre ou d'une variable locale.
Dans votre cas particulier, cela ne fait pas de différence sur le plan fonctionnel comme dans de nombreux autres cas lorsque vous déclarez une variable locale comme const mais cela met une restriction que vous ne pouvez pas modifier cette variable.
la source
Passer un pointeur const à une fonction n'a pas de sens, car il sera de toute façon passé par valeur. C'est juste une de ces choses qui sont autorisées par la conception générale du langage. L'interdire simplement parce que cela n'a pas de sens ne ferait que créer la spécification du langage. plus grand.
Si vous êtes à l'intérieur d'une fonction, c'est bien sûr un autre cas. Avoir un pointeur qui ne peut pas changer ce sur quoi il pointe est une assertion qui rend le code plus clair.
la source
Je suppose qu'un avantage serait que le compilateur peut effectuer des optimisations plus agressives à l'intérieur de la fonction en sachant que ce pointeur ne peut pas changer.
Il évite également par exemple. passer ce pointeur à une sous-fonction qui accepte une référence de pointeur non-const (et pourrait donc changer le pointeur comme
void f(int *&p)
), mais je suis d'accord, que l'utilité est quelque peu limitée dans ce cas.la source
Un exemple où un pointeur const est hautement applicable peut être démontré ainsi. Considérez que vous avez une classe avec un tableau dynamique à l'intérieur, et que vous souhaitez transmettre l'accès utilisateur au tableau, mais sans leur accorder les droits de modifier le pointeur. Considérer:
Ce qui produit:
Mais si nous essayons ceci:
On a:
Il est donc clair que nous pouvons modifier le contenu du tableau, mais pas le pointeur du tableau. Bon si vous voulez vous assurer que le pointeur a un état cohérent lors du renvoi à l'utilisateur. Il y a cependant un hic:
Nous pouvons toujours supprimer la référence mémoire du pointeur, même si nous ne pouvons pas modifier le pointeur lui-même.
Donc, si vous voulez que la référence mémoire pointe toujours vers quelque chose (IE ne doit jamais être modifié, de la même manière qu'une référence fonctionne actuellement), alors c'est hautement applicable. Si vous voulez que l'utilisateur ait un accès complet et le modifie, alors non-const est pour vous.
Éditer:
Après avoir noté le commentaire okorz001 de ne pas pouvoir attribuer en raison de GetArray () étant un opérande de bonne valeur, son commentaire est tout à fait correct, mais ce qui précède s'applique toujours si vous deviez renvoyer une référence au pointeur (je suppose que j'ai supposé que GetArray était référant une référence), par exemple:
Reviendra dans le premier entraînant une erreur:
Mais le second se produira joyeusement malgré les conséquences potentielles sur le dessous.
Évidemment, la question se posera «pourquoi voudriez-vous renvoyer une référence à un pointeur»? Il existe de rares cas où vous devez attribuer de la mémoire (ou des données) directement au pointeur d'origine en question (par exemple, créer votre propre front-end malloc / free ou new / free), mais dans ces cas, il s'agit d'une référence non-const . Une référence à un pointeur const Je n'ai pas rencontré de situation qui le justifierait (à moins que peut-être en tant que variables de référence const déclarées plutôt que de types de retour?).
Considérez si nous avons une fonction qui prend un pointeur const (par opposition à un qui ne le fait pas):
L'erreur dans le const produit ainsi le message:
Ce qui est bien car nous ne voulons probablement pas faire cela, à moins que nous ne voulions causer les problèmes indiqués dans les commentaires. Si nous éditons le décrément dans la fonction const, ce qui suit se produit:
Clairement, même si A est «Data [1]», il est traité comme «Data [0]» car le pointeur NonConst a permis l'opération de décrémentation. Avec le const implémenté, comme une autre personne l'écrit, nous détectons le bogue potentiel avant qu'il ne se produise.
Une autre considération principale, est qu'un pointeur const peut être utilisé comme une pseudo référence, en ce que la chose vers laquelle la référence pointe ne peut pas être modifiée (on se demande si c'est peut-être ainsi qu'elle a été implémentée). Considérer:
Lors de la tentative de compilation, génère l'erreur suivante:
Ce qui est probablement une mauvaise chose si une référence constante à A était souhaitée. Si
B = NULL
est commenté, le compilateur se fera un plaisir de nous laisser modifier*B
et donc A. Cela peut ne pas sembler utile avec ints, mais considérez si vous aviez une seule position d'une application graphique où vous vouliez un pointeur non modifiable qui y faisait référence que vous pourriez passer autour.Son utilisation est variable (excusez le jeu de mots involontaire), mais utilisé correctement, c'est un autre outil dans la boîte pour aider à la programmation.
la source
Temp.GetArray() = NULL
échoue parce queTemp.GetArray()
c'est une rvalue, pas parce qu'elle est qualifiée const. De plus, je crois que le qualificatif const est supprimé de tous les types de retour.Il n'y a rien de spécial à propos des pointeurs où vous ne voudriez jamais qu'ils soient const. Tout comme vous pouvez avoir des
int
valeurs constantes de membre de classe , vous pouvez également avoir des pointeurs constants pour des raisons similaires: vous voulez vous assurer que personne ne change jamais ce qui est pointé. Les références C ++ abordent quelque peu cela, mais le comportement du pointeur est hérité de C.la source
Je pense que cela empêcherait le code d'incrémenter ou de décrémenter le pointeur dans le corps de la fonction.
la source
Types de déclaration de toutes les variables comme-
(1) Déclaration d'une variable constante.
DataType const varibleName;
const dataType* PointerVaribleName=&X;
dataType* const PointerVaribleName=&X;
la source