Je ne sais toujours pas où placer const
dans les pointeurs avec plus d'une indirection. Quelqu'un peut-il clarifier?
Par exemple. pour le moment, j'ai besoin d'un pointeur sur le pointeur const, c'est-à-dire une telle variable int ** ppTargets
que je peux lui attribuer une variable int * pTargets
, comme:
XXX
Le code ci-dessus manque de const
. Donc dans foo
je veux que pTargets
pointe vers la mémoire constante et ne puisse pas être assigné après l'initialisation (pour qu'on ne puisse pas écrire par exemple pTargets ++
), ce serait int const * const pTargets = / * attribué une fois * /
. Ensuite, je veux déclarer ppTargets
que ppTargets
lui-même peut être attribué, mais que * ppTargets
ne peut être lu que.
En d'autres termes, dans le code appelant que je veux:
int foo(int *const *const ppTargets)
j'ai essayé de déclarer foo
comme suit, mais obtenir une erreur que vous ne pouvez pas attribuer à une variable qui est const
:
int const* pTargets; foo(&pTargets);
7 Réponses :
Je veux donc que pTargets pointe vers la mémoire constante et soit const lui-même
Ensuite, je veux déclarer ppTargets que ppTargets lui-même peut être affecté, mais alors * ppTargets ne peut être lu.
Pour plus de clarté, laissez int const *
être Ptr
et laissez int const * const
(c'est-à-dire Ptr const
) soit CPtr
.
Tout comme vous avez correctement écrit int const * const pTargets
(c'est-à-dire CPtr
) lorsque vous vouliez un pointeur const vers const int, il en va de même lorsque vous voulez un non-const pointeur vers const pointeur vers const int (c'est-à-dire le type de & pTargets
ie CPtr *
), vous avez besoin de int const * const * ppTargets
. Notez qu'un Ptr *
sera implicitement converti en CPtr *
.
Votre tentative int * const * const ppTargets
serait un pointeur const vers un pointeur const vers non-const int. Étant donné que le type est un pointeur const, il ne peut pas être attribué, ce qui est en contradiction avec vos besoins.
Plus généralement, une règle empirique simple est de lire les déclarations de type pointeur C de droite à gauche, et constness s'applique à la gauche du mot-clé (sauf s'il s'agit du jeton le plus à gauche, auquel cas il s'applique à la droite).
Maintenant que nous avons trouvé un type qui répond à vos exigences, laissez-moi attirer votre attention sur votre implémentation de foo
qui fait * ppTargets = pTargets
. Cela est en contradiction avec l'exigence de " * ppTargets
ne peut être lu que ".
Puisque pTargets
est un const int *
, son adresse est un const int **
, qui est le type que vous voulez pour la fonction paramètre:
const int *foo2() { int *pTargets = malloc(sizeof(int)*4); pTargets[0] = 1; pTargets[1] = 2; pTargets[2] = 3; pTargets[3] = 4; return pTargets; } int main() { int const * const pTargets = foo2(); return 0; }
EDIT:
Si la variable à définir est définie comme int const * const pTargets;
, la seule façon de la définir est quand il est initialisé. Vous pouvez alors faire ceci à la place:
int foo(const int **ppTargets) { int *pTargets = malloc(sizeof(int)*4); pTargets[0] = 1; pTargets[1] = 2; pTargets[2] = 3; pTargets[3] = 4; *ppTargets = pTargets; return 37; } int main() { int const *pTargets; foo(&pTargets); return 0; }
Maintenant, comment éviter ppTargets ++
dans le code de foo
? Cela signifie que la variable elle-même est également const
.
@SergeRogatch ppTargets
est une variable locale de foo
, donc si vous la modifiez, elle n'est pas reflétée dans la fonction appelante.
L'appel de ce foo
ne fonctionnera pas si " pTargets
[pointe] vers la mémoire constante et [est] const lui-même " i > comme le dit Serge dans la question. Votre pTargets
n'est pas const.
Pourquoi est-ce que pTargets
est déclaré comme int const *
et pas const int *
dans main, alors?
@zmbq Ce sont des équivalents.
Eh bien, je savais que je suppose, mais cet article m'a fait remettre en question tout ce que je sais sur les pointeurs const C ++. Je regrette également de me plaindre qu'il n'y a pas de références const en C #.
Ce que vous recherchez est int const * const * ppTarget
. Non, attendez, vous recherchez int const ** const ppTarget
. Non non, c'est int * const * const * ppTarget
.
Il y a de fortes chances que l'un d'entre eux soit correct (je parie le premier). Cependant, vous ne voulez pas que les gens lisant votre code devinent ce que vous voulez dire. C'est trop déroutant. C ++ peut vous le faire.
Ce que vous devez faire, c'est utiliser des typedef
s pour vous assurer que les personnes qui lisent le code comprennent ce que vous voulez.
typedef const int *CINT_PTR; CINT_PTR pTarget = ....; CINT_PTR *ppTarget = &pTarget;
Le second est correct, comme l'explique John Burger.
Cela renforce simplement mon point de vue. Il a fallu trois réponses de personnes ayant une réputation de débordement de pile à 5 chiffres (dont deux près de 100 000) pour y parvenir. N'écrivez pas de code qui ne peut être compris que par un groupe d'experts après un débat.
Je lis toujours les définitions C / C ++ du nom de variable le plus à droite vers la gauche.
Donc:
const char * p;
p
est un pointeur vers un char
qui est const
Ainsi, p
peut être modifié, mais * p
ne le peut pas.
const char * * const p = & a;
p
est un pointeur const
vers un pointeur vers un char
qui est const
.
Donc p
ne peut pas être modifié (donc je l'ai initialisé); * p
peut; mais ** p
ne peut pas.
[EDIT - tableaux ajoutés par souci d'exhaustivité]
const char * * const p [4] = {& a, & b, & c, & d};
p
est un tableau de 4 éléments de pointeurs const
vers ...
Ce. Ainsi, l'OP veut int foo (const int ** const ppTargets)
(ou int foo (int const ** const ppTargets)
, ce qui revient au même).
OTOH, le niveau supérieur const
dans les paramètres n'a en fait aucun sens pour les types copiables.
@TonyK non, OP a dit "que ppTargets
lui-même peut être attribué" . Vous ne pouvez pas attribuer un const int ** const
. De plus, l'adresse de int const * const
a un type différent.
@eerorika: oui, en relisant la question, vous avez raison. Mais l'OP se contredit, je pense. Alors je vais me retirer de celui-ci.
Je veux donc que
pTargets
pointe vers la mémoire constante et soitconst
lui-même, ce seraitint const * const pTargets = / * assigné une fois * /
. Ensuite je veulent déclarerppTargets
queppTargets
lui-même peut être attribué, mais alors* ppTargets
ne peut être lu.Malheureusement, cela n'a aucun sens. Votre exemple de code est affecté à
* ppTargets
, comme cela semble être l'objectif principal de la fonctionfoo ()
. Si* ppTargets
peut être attribué une fois, il peut être attribué à nouveau.On ne sait pas pourquoi vous voulez le
foo ()
> pTargets pour êtreconst
, au lieu de simplement ne pas le modifier, mais vous pouvez attribuer une valeurconst
à un objet du non-correspondant const
- type qualifié. Ainsi, ce que vous recherchez peut êtreint const* pTargets; foo(&pTargets);Et cela semble être cohérent avec votre utilisation prévue:
En d'autres termes, dans le code d'appel que je veux:
int foo(int const **ppTargets) { int const * const pTargets = /* calculate here */; *ppTargets = pTargets; return 37; // just e.g. }Pour tout type
T
, le type d'un pointeur vers unT
peut être orthographiéT *
. En particulier, le type de ce& pTargets
estint const **
(vous semble familier?), Et c'est le type approprié pour un paramètre de fonction à travers lequel la fonction devrait être en mesure de définir la valeur despTargets
de l'appelant .Et encore une fois, appeler
foo ()
pour lui demander de définir la valeur despTargets de l'appelant
semble être exactement le point. Sifoo ()
était censé être empêché de le faire, alors l'approche idéale serait de passerpTargets
lui-même (par valeur), au lieu de passer son adresse et de se disputer const qualificatifs.
Cela ne sera pas compilé car vous ne pouvez pas attribuer à * ppTargets
Oui, @dbush. Je me suis laissé décourager par la première partie de la demande du PO, qui concerne quelque chose qui ne peut pas réellement fonctionner. J'ai substantiellement réécrit cette réponse pour résoudre le problème de la demande du PO et présenter quelque chose qui fonctionne . Je suis sûr que vous ne serez pas surpris que la signature de fonction qui fonctionne soit également celle que vous avez présentée.
Après les entrées des autres, en particulier la règle Clockwise / Spiral par @Mahesh, ainsi que quelques débats, j'ai compris comment lire et écrire facilement de telles choses.
Nous devrions examiner ce qui peut et ce qui ne peut pas être modifié. Considérez donc la déclaration sans const
s: int ** ppTargets
. Nous voulons que ppTargets
ne puisse pas être modifié lui-même, tandis que * pTargets
peut être modifié, tandis que ** pTargets
ne peut pas être modifié.
Ensuite, appliquez ces observations de droite à gauche:
int const * * const ppTargets
.
Le const
le plus à droite indique que ppTargets ++
n'est pas possible.
Ensuite, l'absence de const
au milieu indique que (* ppTargets) = pTargets
est possible.
Puis un autre const
le plus à gauche dit que (** ppTargets) ++
n'est pas possible.
La formalisation de base pour l'indirection est à mon avis
int foo(const int* const* const p); {// equal leftmost qualifier const int* p = nullptr; const int** p1 = &p; // 2nd and 3rd qualifiers are less restrictive foo(p1); const int* const* p2 = &p; // 2nd qualifier is equal, 3rd one (implicit read-write) is less restrictive foo(p2); const int* const* const p3 = &p; // 2nd and 3rd qualifiers are equal foo(p3); } {// less restrictive leftmost qualifier of p int* p = nullptr; int** p1 = &p; // 2nd and 3rd qualifiers are less restrictive foo(p1); int* const* p2 = &p; // 2nd qualifier is equal, 3rd one (implicit read-write) is less restrictive foo(p2); int* const* const p3 = &p; // 2nd and 3rd qualifiers are equal foo(p3); }
où
est lui-même une zone mémoire.
Pour une double indirection, l'expression devient
(read-only|read-write) <memory zone> * (read-only|read-write) <memory zone/pointer-level2> * (read-only|read-write) <pointer-level1>
Ce qui rend les choses plus difficiles à comprendre, c'est la possibilité de placer des qualificatifs (par exemple. lecture seule
) avant OU après
sur la taille de gauche du symbole *
. Sur le côté droit du symbole *
, le ou les qualificatifs peuvent être placés uniquement avant
.
En C ++, lecture seule
signifie const et lecture-écriture
est le qualificatif implicite.
Ainsi on peut avoir:
char * p
pointeur de lecture-écriture vers la zone de mémoire char
lecture-écriture const char * p
pointeur en lecture-écriture vers la zone de mémoire lecture seule char
char * const p
lecture seule pointeur vers la zone mémoire char
lecture-écriture const char * const p
lecture seule pointeur vers lecture seule char
zone mémoire li >
Ensuite, nous pouvons déplacer const
après le type de base résultant des déclarations équivalentes:
char const * p
pointeur en lecture-écriture vers la zone de mémoire lecture seule char
char const * const p
lecture seule pointeur vers lecture seule char
zone de mémoire li >
La conversion de pointeur est autorisée avec un qualificatif égal ou un qualificatif moins restrictif dans le pointeur source pour chaque niveau d'indirection par rapport au pointeur de destination.
En conséquence, les cas suivants sont valides:
(read-only|read-write) <memory zone> * (read-only|read-write) <pointer>
Dans votre cas, le qualificatif le plus à gauche du pointeur passé en argument ( & pTargets
) n'est pas égal ou moins restrictif que le qualificatif le plus à gauche du pointeur de Fonction foo
.
utilisez
typedef
pour passer de vos types simples à ceux que vous voulez réellement utiliserRègle sens horaire / spirale
Vous vous contredisez:
Donc je veux que pTargets pointe vers la mémoire constante et soit const lui-même, ce serait int const * const pTargets
... puisEn d'autres termes, dans l'appel code que je veux: int const * pTargets;
Alors, quel est-il? Est-ce quepTargets
est censé être const ou non?@eerorika, en d'autres termes, je souhaite interdire l'affectation à
pTargets
et** pTargets
, mais pas à* pTargets
. Je ne vois pas encore de contradiction, laissez-moi réfléchir à ce qui pourrait être mal compris ...@SergeRogatch
Je veux interdire l'affectation aux pTargets
est en contradiction avecint const * pTargets
carint const *
peut être attribué.@eerorika, maintenant je vois la confusion: c'est différent
pTargets
entre le code de l'appelant et celui de l'appelé.c'est du code C ++, pas C. (Ce sont deux langages différents) Suggère fortement de supprimer la balise 'c'
@ user3629249, je pensais que c'était à la fois du code C et C ++. Pourriez-vous expliquer ce qui en fait uniquement C ++?