Question

J'ai toujours gâcher comment utiliser const int*, const int * const et int const * correctement. Y at-il un ensemble de règles définissant ce que vous pouvez et ne pouvez pas le faire?

Je veux savoir toutes les choses à faire et tous à ne pas faire en termes de missions, en passant aux fonctions, etc.

Était-ce utile?

La solution

Lire l'arrière (comme conduit par Clockwise / Règle Spiral ):

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

Maintenant, la première const peut être de part et d'autre 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 - un pointeur const à un pointeur vers un int
  • int * const * - un pointeur vers un pointeur const à un int
  • int const ** - un pointeur vers un pointeur sur un const int
  • int * const * const - un pointeur const à un pointeur const à un int
  • ...

Et pour que nous soyons 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 à un nombre entier constant. Cela vous permet de changer 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 vers un const char. Vous pouvez changer quelle chaîne vous pointez, mais vous ne pouvez pas modifier le contenu de ces chaînes. Ceci est important lorsque la chaîne elle-même est dans le segment de données d'un programme et ne doit pas être modifié.

bar est un pointeur constante ou fixe à une valeur qui peut être modifiée. C'est comme une référence sans sucre syntaxique supplémentaire. De ce fait, vous généralement utiliser une référence où vous utilisez un pointeur T* const à moins que vous devez autoriser les pointeurs NULL.

Autres conseils

Pour ceux qui ne connaissent pas Clockwise / Spirale Règle: Commencez par le nom de la variable, déplacer clockwisely (dans ce cas, revenir en arrière) à l'autre pointeur ou type . Répéter jusqu'à la fin de l'expression.

Voici une démo:

pointeur vers int

pointeur const int const

pointeur vers int const

pointeur sur const int

pointeur const int

Je pense que tout est déjà répondu ici, mais je veux juste ajouter que vous devriez prendre garde de typedefs! Ils ne sont pas seulement du texte de remplacement.

Par exemple:

typedef char *ASTRING;
const ASTRING astring;

Le type de astring est char * const, pas const char *. C'est une raison pour laquelle je tends toujours à mettre const à droite du type, et jamais au début.

Comme à peu près tout le monde 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 points à un X qui est const":. L'objet X ne peut pas être modifié par p

  •   
  • X* const p signifie "p est un pointeur const à un X qui est non-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 à un X qui est const": vous ne pouvez pas changer le pointeur p lui-même, et vous ne pouvez changer l'objet X via p

  • .   
  1. La référence constante:

    Une référence à une variable (ici int), qui est constante. Nous passons la variable comme une 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. On peut accidentellement changer la variable principale grâce à notre accès à l'alias, donc nous rendre constante pour éviter cet effet secondaire.

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

    Une fois les points de pointeur constant à une variable, alors il ne peut pas pointer vers une autre variable.

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

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

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. pointeur constant à une constante

    Un pointeur constant à une constante est un pointeur qui ne peut ni changer l'adresse qu'il pointe vers et elle ne peut pas changer la valeur maintenue à cette adresse.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    

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

  • const int* est le même que int const* et des moyens "pointeur à int constant" .
  • const int* const est le même que int const* const et des moyens "pointeur constant int constant" .

Edit: Pour le Dos and Don'ts, si cette réponse ne suffit pas, pourriez-vous être plus précis sur ce que vous voulez?

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

En bref, je trouve la meilleure façon de se rappeler la règle est que le « const » va après la chose elle s'applique. Donc, dans votre question, "int const *" signifie que l'int est constante, tandis que "int * const" signifierait que le pointeur est constant.

Si quelqu'un décide de le mettre tout à l'avant (par exemple: « const int * »)., À titre d'exception dans ce cas, il 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 qu'il est une exception, et confond les choses ainsi.

Simple utilisation de const.

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

const int Constant1=96; 

va créer un nombre entier constant, appelé unimaginatively Constant1, avec la valeur 96.

Ces constantes sont utiles pour les paramètres qui sont utilisés dans le programme mais ne pas besoin d'être changé après que le programme est compilé. Il a un avantage pour les programmeurs sur la commande C préprocesseur #define en ce qu'il est entendu et utilisé par le compilateur lui-même, non seulement 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 également avec des pointeurs, mais il faut être prudent lorsque const pour déterminer si le pointeur ou ce qu'il pointe est constante ou les deux. Par exemple:

const int * Constant2 

déclare que Constant2 est pointeur variable à un nombre entier constant et:

int const * Constant2

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

int * const Constant3

déclare que Constant3 est pointeur constant à un nombre entier variable et

int const * const Constant4

déclare que Constant4 est pointeur constant à un nombre entier constant. Fondamentalement « const » applique à tout ce qui est à sa gauche immédiate (autre que s'il n'y a rien dans ce cas, il applique à tout ce qui est son droit immédiat).

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

J'ai eu le même doute que vous jusqu'à ce que je suis tombé sur cette

Il est simple mais difficile. S'il vous plaît noter que nous pouvons échanger le qualificatif de const avec tout type de données (int, char, float, etc.).

Voyons voir 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 un entier constant]


int *p const ==> Mauvais Déclaration. Compilateur génère une erreur de syntaxe.

int *const p ==> p est en lecture seule [p est un pointeur constant à un nombre entier]. Comme pointeur p ici est en lecture seule, la déclaration et la définition devraient être en même endroit.


const int *p const ==> Mauvais Déclaration. Compilateur génère 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 à un nombre entier constant]. Comme pointeur p ici est en lecture seule, la déclaration et la définition devraient être en même endroit.


int const *p const ==> Mauvais Déclaration. Compilateur génère une erreur de syntaxe.

int const int *p ==> Mauvais Déclaration. Compilateur génère 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 à un nombre entier constant]. Comme pointeur p ici est en lecture seule, la déclaration et la définition devraient être en même endroit.

Il y a beaucoup d'autres points de subtils entourant la correction const en C ++. Je suppose que la question ici est tout simplement plus au sujet de C, mais je vais vous donner quelques exemples liés puisque la balise C ++:

  • Vous passez souvent de grands arguments comme des chaînes 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 n'a pas 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érence TYPE const &. Exemple:

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

  • Il y a des situations communes où à la fois la valeur de retour et la méthode doit être const. Exemple:

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

    En fait, les méthodes const ne doivent pas renvoyer des données internes de classe comme une référence à la non-const.

  • Par conséquent, il faut souvent créer à la fois un const et une méthode non-const utilisant const surcharge. 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 des fonctions const en C, les fonctions non-membres ne peuvent pas être eux-mêmes const en C ++, les méthodes const pourraient avoir des effets secondaires, et le compilateur ne peut pas utiliser les fonctions const pour éviter les appels de fonction en double. En fait, même une simple référence int const & pourrait assister à la valeur à laquelle il se réfère changer ailleurs.

Pour moi, la position de const savoir si elle apparaît à gauche ou à droite ou à gauche et à droite par rapport à la * me aide à comprendre la signification réelle.

  1. A const à la gauche de * indique que l'objet pointé par le pointeur est un objet const.

  2. A const à droite de * indique que le pointeur est un pointeur de const.

Le tableau suivant est extrait de Stanford CS106L standard C ++ Programming Laboratory Course Reader.

Le const avec l'int sur les deux côtés fera pointeur int constante :

const int *ptr=&i;

ou

int const *ptr=&i;

const après * fera pointeur constant int :

int *const ptr=&i;

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

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

Dans ce cas, tous sont pointeur vers constante entière et ptr2 est pointeur constante à constante entière . Mais ptr1 n'est pas constante pointeur:

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

Cette adresse la plupart du temps la deuxième ligne:. Les meilleures pratiques, les affectations, les paramètres de la fonction etc

La pratique générale. Essayez de faire tout const que vous pouvez. Ou mettre une autre façon, tout faire const pour commencer, puis retirez exactement l'ensemble minimal de consts nécessaires pour permettre au programme de fonctionner. Ce sera une grande aide dans la réalisation de const-exactitude, et faire en sorte que les bugs subtils ne soient introduits pas quand les gens essaient et affecter des choses qu'ils ne sont pas censés modifier.

Évitez const_cast <> comme la peste. Il y a un ou deux cas d'utilisation légitimes, mais ils sont très peu nombreux. Si vous essayez de changer un objet const, vous allez faire beaucoup mieux pour trouver celui qui l'a déclaré const dans le premier rythme et discuter de la question avec eux pour parvenir à un consensus sur ce qui devrait se produire.

Ce qui conduit très facilement dans les affectations. Vous pouvez attribuer en quelque chose que si elle est non-const. Si vous souhaitez affecter en quelque chose qui est const, voir ci-dessus. Rappelez-vous que dans les déclarations int const *foo; et int * const bar; choses différentes sont const -. D'autres réponses ici ont couvert cette question admirablement, je ne vais pas aller dans ce

Paramètres de fonction:

passage par valeur: par exemple, void func(int param) vous ne se soucient pas d'une façon ou l'autre sur le site d'appel. L'argument peut être fait que dans certains cas l'utilisation pour déclarer la fonction comme void func(int const param) mais qui n'a aucun effet sur l'appelant, uniquement sur la fonction elle-même dans la mesure où toute valeur est passé ne peut être changé par la fonction lors de l'appel.

passer par référence: par exemple, void func(int &param) Maintenant, il fait une différence. Comme vient de déclarer func est autorisé à changer param, et un site d'appel devrait être prêt à faire face aux conséquences. Modification de la déclaration void func(int const &param) modifie le contrat, et garantit que func aujourd'hui ne peut pas changer param, ce qui signifie ce qui est transmis est ce qui va revenir sur. Comme d'autres ont noté ce qui est très utile pour passer à peu de frais un grand objet que vous ne voulez pas changer. Le passage d'une référence est beaucoup moins cher que le passage d'un grand objet en valeur.

Passez 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 nullptr à moins d'une autre garantie contractuelle assure func qu'il ne recevra jamais un nullptr dans param.

Article d'opinion sur ce sujet. Prouver correcte, en un cas comme celui-ci est diablement difficile, il est tout simplement trop facile de faire une erreur. Alors ne prenez pas de risques, et toujours vérifier les paramètres de pointeur pour nullptr. Vous vous épargnerez la douleur et de la souffrance et du mal à trouver des bogues à long terme. Quant au coût du chèque, il est la saleté pas cher, et dans les cas où l'analyse statique intégrée dans le compilateur peut le gérer, l'optimiseur élidera de toute façon. Sur le lien Activer Temps de génération de code pour MSVC ou WOPR (je pense) pour GCC, et vous obtenez l'échelle de programme, à savoir même dans les appels de fonction qui traversent une limite de module de code source.

A la fin de la journée, tous les rend au-dessus d'un cas très solide préférer toujours des références à des pointeurs. Ils sont tout simplement plus en sécurité tout autour.

Juste pour être complet C suivant les explications des autres, pas sûr C ++.

  • p - pointeur vers pointeur
  • p - pointeur
  • données - la chose a, dans les exemples x
  • gras - en lecture seule variable

pointeur

  • 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 un pointeur

  1. données pp p - int **pp;
  2. p données de p - int ** const pp;
  3. p p données - int * const *pp;
  4. p p données - int const **pp;
  5. p p données - int * const * const pp;
  6. p p données - int const ** const pp;
  7. p p données - int const * const *pp;
  8. p 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éréférencement

Il suffit de continuer, mais peut l'humanité que vous excommunier.

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

printf("%d \n", ****pppp);
  • si const est à gauche de *, il se réfère à la valeur (peu importe que ce soit const int ou int const)
  • si const est à droite de *, il fait référence au pointeur lui-même
  • il peut être les deux en même temps

Un point important: const int *p ne signifie pas la valeur que vous faites référence est constante !! . Cela signifie que vous ne pouvez pas changer par ce pointeur . La valeur elle-même peut être modifiée. 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, afin de garantir que la fonction ne peut pas changer les arguments passés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top