3
votes

Pointeur C ++ vers pointeur const

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);


8 commentaires

utilisez typedef pour passer de vos types simples à ceux que vous voulez réellement utiliser


Rè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 ... puis En d'autres termes, dans l'appel code que je veux: int const * pTargets; Alors, quel est-il? Est-ce que pTargets 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 avec int const * pTargets car int 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 ++?


7 Réponses :


0
votes

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 ".


0 commentaires

1
votes

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;
}


6 commentaires

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 " 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 #.



4
votes

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;


2 commentaires

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.



5
votes

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 ...


4 commentaires

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.



1
votes

Je veux donc que pTargets pointe vers la mémoire constante et soit const lui-même, ce serait int const * const pTargets = / * assigné une fois * / . Ensuite je veulent déclarer ppTargets que ppTargets 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 fonction foo () . Si * ppTargets peut être attribué une fois, il peut être attribué à nouveau.

On ne sait pas pourquoi vous voulez le foo () > pTargets pour être const , au lieu de simplement ne pas le modifier, mais vous pouvez attribuer une valeur const à un objet du non- correspondant const - type qualifié. Ainsi, ce que vous recherchez peut être

int 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 un T peut être orthographié T * . En particulier, le type de ce & pTargets est int 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 des pTargets de l'appelant .

Et encore une fois, appeler foo () pour lui demander de définir la valeur des pTargets de l'appelant semble être exactement le point. Si foo () était censé être empêché de le faire, alors l'approche idéale serait de passer pTargets lui-même (par valeur), au lieu de passer son adresse et de se disputer const qualificatifs.


2 commentaires

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.



0
votes

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.


0 commentaires

0
votes

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);
}

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 .


0 commentaires