const provoquant un type de pointeur incompatible. Pourquoi seulement pour les doubles pointeurs?

Cette question a été abordée ici .

Le doublon suggéré et les réponses données actuellement n’indiquent pas pourquoi les exemples donnés en premier ne posent aucun problème Principalement pourquoi le raisonnement ne:

const int ** is a pointer to const int * qui est différent de juste int*

appliquer également pour:

const int * is a pointer to const int qui est différent de juste int


Je l’aborde sous un angle différent pour obtenir une autre explication, espérons-le.

Le code avec les exemples.

 #include  void f_a (int const a){ /* * Can't do: * a = 3; //error: assignment of read-only parameter 'a' * * Explanation: I can't change the value of a in the scope of the function due to the const */ printf("%d\n", a); } void f_ptr_a_type1 (int const * ptr_a){ /* * Can do this: * ptr_a' = 0x3; * which make dereferencig to a impossible. * printf("%d\n", * ptr_a'); -> segfault * But const won't forbid it. * * Can't do: * *ptr_a' = 3; //error: assignment of read-only parameter '* ptr_a' * * Explanation: I can't change the value of a by pointer dereferencing and addignment due to the int const */ } void f_ptr_a_type2 (int * const ptr_a){ /* * Can do this: * *a = 3; * * Can't do: * ptr_a = 3; //error: assignment of read-only parameter 'ptr_a' * * Explanation: I can't change the value because the const is protecting the value of the pointer in the funcion scope */ } void f_ptr_ptr_a (int const ** ptr_ptr_a){ /* * Can do this: * ptr_ptr_a = 3; * * ptr_ptr_a = 0x3; * * Can't do: * ** ptr_ptr_a = 0x3; //error: assignment of read-only parameter '**ptr_a' * * Explanation: Makes sense. Just follows the pattern from previous functions. */ } int main() { int a = 7; f_a(a); int * ptr_a = &a; f_ptr_a_type1(&a); f_ptr_a_type2(&a); int ** ptr_ptr_a = &ptr_a; f_ptr_ptr_a(ptr_ptr_a); //warning: passing argument 1 of 'f_ptr_ptr_a' from incompatible pointer type [-Wincompatible-pointer-types] } 

La réponse acceptée largement acceptée va quelque chose comme ceci:

int ** n’est pas identique à const int ** et vous ne pouvez pas le lancer en toute sécurité

Ma question est pourquoi la fonction s’inquiète- t-elle soudainement ?

Il ne s’est pas plaint ici que int n’est pas int const :

 int a = 7; f_a(a); 

Il ne s’est pas plaint ici car int * n’est ni int const * ni int * const :

 int * ptr_a = &a; f_ptr_a_type1(&a); f_ptr_a_type2(&a); 

Mais tout à coup, il commence à se plaindre dans le cas du double pointeur.

  • Vous cherchez des explications à l’aide de cette terminologie et de cet exemple?

  • Pourquoi la fonction commence-t-elle soudain à se préoccuper des permissions d’écriture pour quelque chose qui est en dehors de sa scope?

Une conversion, par exemple, de char * en const char * est toujours sûre. Grâce au const char * , les données pointées ne peuvent pas être modifiées, et c’est tout.

D’autre part, une conversion de char ** en const char ** peut être dangereuse , elle n’est donc pas autorisée implicitement. Au lieu de l’expliquer, considérons le code suivant:

 void foo(const char **bar) { const char *str = "test ssortingng"; *bar = str; // perfectly legal } int main(void) { char *teststr[] = {0}; foo((const char **)teststr); // now teststr points to a `const char *`! *teststr[0] = 'x'; // <- attempt to modify read-only memory // ok in this line, there's no const qualifier on teststr! } 

Si la conversion de char ** en const char ** lors de l'appel de foo() était implicite, vous disposeriez d'un moyen implicite de convertir un const .

Le langage n’autorise pas les conversions non sécurisées, c’est-à-dire qu’il est possible de transformer un object pointeur sur const en un object pointeur sur non const .

Il se moque des conversions sûres, c’est-à-dire celles où vous appliquez une qualification de const plus ssortingcte que ce qui a été passé. Par exemple, lorsque l’object est vraiment accessible en écriture, mais que vous le transmettez à une fonction qui n’a besoin que de le lire, il serait inutile de se plaindre à ce sujet.

Vos exemples montrent ce dernier:

 f_ptr_a_type1(&a); 

Ceci dit ‘Prends une vue const de cet int que j’ai déclaré non- const

 f_ptr_a_type2(&a); 

Cela dit simplement ‘Voici un pointeur sur ce même const int non- const int , et vous en ferez une copie, qui sera const dans le corps de la fonction (le pointeur, pas l’ int )’

Quant à celui qui se trompe:

 f_ptr_ptr_a(ptr_ptr_a); 

Ceci résulte de ce que certains types de programmeurs ont dit: les types référés sont différents, et alors que C / C ++ permettra de passer un pointeur à un point où un pointeur à un non est prévu, il ne “cascade” pas cette conversion. à plus d’un niveau de pointeur, car cela pourrait être dangereux comme l’a illustré Felix Palmen depuis.

Permet de le décomposer un peu:

  • const int ** est un pointeur sur const int * .

  • int ** est un pointeur sur int * .

En d’autres termes, const int ** est un pointeur sur une chose et int ** est un pointeur sur quelque chose de différent.

Pourquoi la fonction commence-t-elle soudain à se préoccuper des permissions d’écriture pour quelque chose qui est en dehors de sa scope?

Ce n’est pas que la plainte vienne du sharepoint vue du paramètre function. Le paramètre se comportera comme prévu dans l’étendue de la fonction et n’est pas affecté par ce qui se passe avec la variable avant d’entrer dans l’étendue de la fonction.

 void f_ptr_ptr_a (int const ** ptr_ptr_a){ /* * Can't do: * ** ptr_ptr_a = 3; //error: assignment of read-only parameter '**ptr_a' */ } 

int const ** ptr_ptr_a entre la scope de la fonction en cours de copie en refusant une modification de ** ptr_ptr_a . L’erreur n’a rien à voir avec ce qu’étaient les variables une fois qu’elles ont été copiées par valeur.

L’erreur est due à un cast implicite qui s’est produit lors de l’appel de la fonction. Dissection de l’appel f_ptr_ptr_a(ptr_ptr_a); on a:

 int const ** ptr_ptr_x = ptr_ptr_a; //This line causes the warning f_ptr_ptr_a(ptr_ptr_x); 

Vous cherchez des explications à l’aide de cette terminologie et de cet exemple?

Voyons maintenant l’exemple à la base.

 int main() { int a = 3; int * ptr_a = &a; int ** ptr_ptr_a = &ptr_a; // I promise here **ptr_ptr_a will always be the same int const b = 5; int const * ptr_b = &b; int const ** ptr_ptr_b = &ptr_b; ptr_ptr_b = ptr_ptr_a; // Warning here: -Wincompatible-pointer-types-discards-qualifiers printf("%d\n", ** ptr_ptr_b); // Look at me, I've just changed the value of const ** int. ** ptr_ptr_a = 15; printf("%d\n", ** ptr_ptr_b); // I did it again. } 

Le compilateur nous avertit à cause de la dissortingbution implicite.

 int main() { int const a = 3; int const * ptr_a = &a; int const ** ptr_ptr_a = &ptr_a; // I promise here **ptr_ptr_a will always be the same int const b = 5; int const * ptr_b = &b; int const ** ptr_ptr_b = &ptr_b; ptr_ptr_b = ptr_ptr_a; printf("%d\n", ** ptr_ptr_b); // Look at me, I've just changed the value of const ** int. // And there were no warnings at all. Har har har har! } 

Il y a une conclusion que je peux tirer de cette question et, du sharepoint vue actuel, des complexités inutiles qu’elle introduit.

Gardez toujours à l’esprit que l’ access par déréférencement n’est pas la même chose que l’access direct .

Nous l’avons vu ici. Le qualificatif const fait ce qu’il devrait faire, nous empêchant de modifier ** ptr_ptr_b par le mécanisme de déréférencement. Bien sûr, nous avons apparemment réussi à modifier la valeur en lecture seule, mais c’est simplement parce que nous demandons beaucoup au pauvre const .

Un autre exemple de la réponse de sheu d’ ici

 const char c = 'A'; char* ptr; const char** const_ptr = &ptr; // <-- ILLEGAL, but what if this were legal? *const_ptr = &c; *ptr = 'B'; // <- you just assigned to "const char c" above. printf("%c \n", c); printf("%c \n", *ptr); 

Lorsque vous dites que you just assigned to "const char c" above - you just assigned to "const char c" above , ce n'est pas vrai, c'est simplement l'abstraction de référence qui devient incontrôlable.