60 votes

Pourquoi ne puis-je pas convertir un 'char**' en un 'const char* const*' en C ?

L'extrait de code suivant donne (correctement) un avertissement en C et une erreur en C++ (en utilisant respectivement gcc et g++, testés avec les versions 3.4.5 et 4.2.1 ; MSVC ne semble pas s'en soucier) :

char **a;
const char** b = a;

Je peux comprendre et accepter cela.
La solution C++ à ce problème consiste à transformer b en un const char * const *, ce qui interdit la réaffectation des pointeurs et vous empêche de contourner la const-correctness ( FAQ C++ ).

char **a;
const char* const* b = a;

Cependant, en C pur, la version corrigée (utilisant const char * const *) donne toujours un avertissement, et je ne comprends pas pourquoi. Existe-t-il un moyen de contourner ce problème sans utiliser un cast ?

Pour clarifier :
1) Pourquoi cela génère-t-il un avertissement en C ? Il devrait être entièrement const-safe, et le compilateur C++ semble le reconnaître comme tel.
2) Quelle est la bonne façon de procéder pour accepter ce char** comme paramètre tout en disant (et en faisant en sorte que le compilateur l'applique) que je ne modifierai pas les caractères vers lesquels il pointe ? Par exemple, si je voulais écrire une fonction :

void f(const char* const* in) {
  // Only reads the data from in, does not write to it
}

Et je voulais l'invoquer sur un char**, quel serait le type correct pour le paramètre ?

0 votes

Je suis confus quant à votre question, est-elle : "Comment faire pour que le C ne m'avertisse pas ?" ou "Comment faire pour que le C++ ne fasse pas d'erreur de compilation ?" Ou peut-être que ce n'est ni l'un ni l'autre.

0 votes

Je suis d'accord avec sixlettervariables, cette question doit être clarifiée.

0 votes

La question est "pourquoi je ne peux pas convertir 'char**' en 'char const ?"

59voto

Kevin Points 7334

J'ai eu le même problème il y a quelques années et cela m'a irrité au plus haut point.

Les règles en C sont plus simplement énoncées (c'est-à-dire qu'elles ne listent pas les exceptions comme la conversion char** a const char*const* ). En conséquence, ce n'est tout simplement pas autorisé. Avec le standard C++, ils ont inclus plus de règles pour permettre des cas comme celui-ci.

En fin de compte, c'est juste un problème dans la norme C. J'espère que la prochaine norme (ou rapport technique) y remédiera.

0 votes

Je n'ai certainement pas voté contre ; c'est la réponse la plus pertinente que j'ai obtenue jusqu'à présent. Si j'avais 100 points de réputation, je voterais en faveur de cette réponse.

1 votes

@Kevin : Si vous êtes toujours là, je me demande à quel point vous êtes certain de votre réponse selon laquelle il s'agit simplement d'un problème dans la norme - à l'époque, avez-vous réellement parcouru la norme pour le vérifier ? Si c'est le cas, je peux à peu près clore cette question, car c'est la seule réponse que je vais obtenir.

2 votes

@HappyDude : après avoir lu la norme C, après le premier 'const', tous les paris semblent être perdus, ce qui fait que le compilateur croit qu'il y a des problèmes de const correctness. Kevin a la lecture la plus correcte.

10voto

Fabio Ceconello Points 8662

Pour être considéré comme compatible, le pointeur source doit être const dans le niveau d'indirection immédiatement antérieur. Donc, cela vous donnera l'avertissement dans GCC :

char **a;
const char* const* b = a;

Mais ce ne sera pas le cas :

const char **a;
const char* const* b = a;

Sinon, vous pouvez le lancer :

char **a;
const char* const* b = (const char **)a;

Vous auriez besoin du même casting pour invoquer la fonction f() comme vous l'avez mentionné. Pour autant que je sache, il n'y a aucun moyen de faire une conversion implicite dans ce cas (sauf en C++).

0 votes

Veuillez lire la question plus attentivement. Je suis conscient que le premier extrait de code est incorrect, comme je le dis à la ligne qui le suit immédiatement. Le second, en revanche, devrait être correct (pour autant que je sache). Comme je l'ai mentionné, MSVC ne donne pas d'avertissement, mais gcc le fait.

0 votes

Désolé, je n'ai pas bien expliqué ce que je voulais, et je n'ai pas non plus testé le résultat dans GCC pour m'assurer que les échantillons de code étaient exacts. Je corrige.

0 votes

Merci Fabio, votre réponse éditée est beaucoup plus claire. Je pense que je vais devoir accepter que je dois le mouler.

9voto

Aaron Points 2456

Cependant, en C pur, cela donne toujours un avertissement, et je ne comprends pas pourquoi

Vous avez déjà identifié le problème -- ce code n'est pas const-correct. "Const correct" signifie que, sauf pour const_cast et les casts de style C supprimant const vous ne pouvez jamais modifier une const à travers ces pointeurs ou références constantes.

La valeur de const -correction -- const est là, en grande partie, pour détecter les erreurs des programmeurs. Si vous déclarez quelque chose comme const vous déclarez que vous ne pensez pas qu'il devrait être modifié, ou du moins, que ceux qui ont accès à la base de données devraient le faire. const ne devrait pas pouvoir être modifié. Pensez-y :

void foo(const int*);

Tel que déclaré, foo n'a pas autorisation pour modifier l'entier pointé par son argument.

Si vous n'êtes pas sûr de savoir pourquoi le code que vous avez posté n'est pas const -correct, considérez le code suivant, seulement légèrement différent du code de HappyDude :

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

Non- const ne peuvent être convertis en types const que d'une manière particulière, afin d'éviter tout contournement de l'obligation d'utiliser des types const. const sur un type de données sans un cast explicite.

Objets initialement déclarés const sont particulièrement spéciales -- le compilateur peut supposer qu'elles ne changent jamais. Cependant, si b peut se voir attribuer la valeur de a sans cast, alors vous pourriez tenter par inadvertance de modifier un fichier const variable. Cela ne romprait pas seulement la vérification que vous avez demandé au compilateur de faire, pour vous empêcher de changer la valeur de cette variable -- cela vous permettrait aussi de rompre les optimisations du compilateur !

Sur certains compilateurs, cela donnera 42 sur certains 43 et d'autres, le programme se bloquera.

Edit-add :

HappyDude : Votre commentaire est juste. Soit le langage C, ou le compilateur C que vous utilisez, traite const char * const * fondamentalement différente de celle que le langage C++ lui réserve. Peut-être envisager de faire taire l'avertissement du compilateur pour cette ligne source uniquement.

5 votes

Ce que vous avez dit est correct, mais cela ne répond pas à la question. Comme je l'ai dit, je comprends pourquoi la conversion d'un char** en un const char** est incorrecte. Ce que je ne comprends pas, c'est pourquoi la conversion d'un char** en un const char* const * est incorrecte. J'ai mis à jour ma question pour l'élucider.

3 votes

Il est intéressant que cette réponse continue d'être votée. Je suppose que beaucoup de gens ne lisent pas au-delà des deux premières lignes de la question, ce que je garderai à l'esprit à l'avenir. C'est un post bien écrit sur un point relativement obscur, mais j'aimerais qu'il soit pertinent :P.

3 votes

@Aaron : votre message n'est pas particulièrement pertinent pour le sujet, mais il est néanmoins bien écrit.

1voto

wnoise Points 6448

C'est ennuyeux, mais si vous êtes prêt à ajouter un autre niveau de redirection, vous pouvez souvent faire ce qui suit pour pousser vers le bas dans le pointeur-à-pointeur :

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

Il a une signification légèrement différente, mais il est généralement réalisable, et il n'utilise pas de plâtre.

0voto

user7116 Points 39829

Je ne parviens pas à obtenir une erreur lors du casting implicite de char** vers const char * const *, du moins sur MSVC 14 (VS2k5) et g++ 3.3.3. GCC 3.3.3 émet un avertissement, dont je ne suis pas sûr qu'il soit correct.

test.c :

#include <stdlib.h> 
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null\n", bar ? "is not" : "is");
}

int main(int argc, char **argv) 
{
    char **x = NULL; 
    const char* const*y = x;
    foo(x);
    foo(y);
    return 0; 
}

Sortie avec compilation en code C : cl /TC /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Sortie avec compilation en code C++ : cl /TP /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Sortie avec gcc : gcc -Wall test.c

test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type

Sortie avec g++ : g++ -Wall test.C

aucune sortie

0 votes

Désolé, j'aurais dû préciser que je compile avec gcc. J'ai mis à jour ma question pour clarifier cela. Heureusement (ou peut-être pas ?), je rencontre rarement des avertissements difficiles à gérer dans msvc.

0 votes

@HappyDude : J'ai mis à jour avec mes résultats de GCC. Et maintenant, je vois ce que vous dites, mais je ne suis pas encore d'accord avec l'avertissement du compilateur. Je vais sortir ma spécification C et voir ce que je trouve.

0 votes

@sixlettervariables : Merci pour la réponse ! Faites-moi savoir si vous trouvez quelque chose, mais je soupçonne que ce que Kevin a dit était correct et que ce n'est tout simplement pas couvert par la norme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X