Question

L'extrait de code suivant (correctement) donne un avertissement en C et une erreur en C ++ (respectivement avec gcc & amp ++, testé 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 à changer b pour qu'il soit un const char * const *, ce qui interdit la réaffectation des pointeurs et vous évite de contourner const-correctness ( FAQ C ++ ).

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

Cependant, en C pur, la version corrigée (avec const char * const *) donne toujours un avertissement, et je ne comprends pas pourquoi. Y a-t-il un moyen de contourner cela sans utiliser de casting?

À préciser:
1) Pourquoi cela génère-t-il un avertissement en C? Il devrait être entièrement sécurisé par le const et le compilateur C ++ semble le reconnaître comme tel.
2) Quelle est la bonne façon d’accepter ce caractère ** comme paramètre en disant (et en faisant appliquer le compilateur) que je ne modifierai pas les caractères qu’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 caractère **, quel serait le type correct pour le paramètre?

Modifier: Merci à ceux qui ont répondu, en particulier à ceux qui ont répondu à la question et / ou donné suite à mes réponses.

J'ai accepté la réponse selon laquelle ce que je veux faire ne peut pas être fait sans un casting, que cela soit ou non possible.

Était-ce utile?

La solution

J'ai eu le même problème il y a quelques années et cela m'a énervé.

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

Au final, il s’agit simplement d’un problème dans la norme C. J'espère que le prochain standard (ou rapport technique) abordera ce sujet.

Autres conseils

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 cela ne va pas:

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

Vous pouvez également le lancer:

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

Vous auriez besoin de la même distribution pour appeler la fonction f () que vous avez mentionnée. Autant que je sache, il n'y a aucun moyen de faire une conversion implicite dans ce cas (sauf en C ++).

> 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" Cela signifie que, sauf pour les castes const_cast et C-style qui suppriment const, vous ne pouvez jamais modifier un objet const avec ces pointeurs ou références const.

La valeur de const-correctness - const sert en grande partie à détecter les erreurs du programmeur. Si vous déclarez quelque chose en tant que const, vous déclarez que vous ne pensez pas qu'il devrait être modifié - ou du moins, ceux qui n'ont accès qu'à la version const ne devraient pas pouvoir le modifier. Considérez:

void foo(const int*);

Comme déclaré, foo n’a pas la permission de modifier le nombre entier pointé par son argument.

Si vous ne savez pas pourquoi le code que vous avez posté n'est pas const-correct, considérez le code suivant, 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;

Les types non-const ne peuvent être convertis en types const que de manière particulière, afin d'éviter tout contournement de 'const' sur un type de données sans transtypage explicite.

Les objets initialement déclarés const sont particulièrement spéciaux - le compilateur peut supposer qu'ils ne changent jamais. Cependant, si vous pouvez affecter la valeur de 'a' à 'b' sans conversion, vous pouvez alors tenter par inadvertance de modifier une variable const. Cela casserait non seulement la vérification que vous demandiez au compilateur de vous empêcher de modifier la valeur de cette variable - cela vous permettrait également de casser les optimisations du compilateur!

Sur certains compilateurs, cela affichera "42", sur certains "43", et d'autres, le programme plantera.

Modifier-ajouter:

HappyDude: Votre commentaire est sur place. Le langage C ou le compilateur C que vous utilisez traitent const char * const * de manière fondamentalement différente de celle du langage C ++. Envisagez peut-être de désactiver l'avertissement du compilateur pour cette ligne source uniquement.

Modifier-supprimer: suppression d'une faute de frappe

Cela est ennuyeux, mais si vous souhaitez ajouter un autre niveau de redirection, vous pouvez souvent procéder comme suit pour descendre dans le pointeur à pointeur:

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

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

Cela a un sens légèrement différent, mais il est généralement réalisable et il n’utilise pas de casting.

Je ne parviens pas à obtenir une erreur lors de la conversion implicite de char ** en const car * const *, du moins pour MSVC 14 (VS2k5) et g ++ 3.3.3. GCC 3.3.3 émet un avertissement, mais je ne suis pas tout à fait sûr que ce 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 la 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 tant que code C ++: cl / TP / W4 / Wp64 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 gcc: gcc -Wall test.c

<*>

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

pas de sortie

Je suis presque sûr que le mot-clé const n'implique pas que les données ne peuvent pas être modifiées / est constant, mais que les données seront traitées en lecture seule. Considérez ceci:

const volatile int *const serial_port = SERIAL_PORT;

qui est un code valide. Comment volatil et const peuvent coexister? Simple. volatile indique au compilateur de toujours lire la mémoire lors de l'utilisation des données et const demande au compilateur de créer une erreur lorsqu'une tentative d'écriture dans la mémoire est effectuée à l'aide du pointeur serial_port.

Est-ce que const aide l'optimiseur du compilateur? Non pas du tout. Comme la constness peut être ajoutée aux données et supprimée de celles-ci par le biais de la conversion, le compilateur ne peut pas déterminer si les données const sont réellement constantes (car la conversion peut être effectuée dans une unité de traduction différente). En C ++, vous disposez également du mot clé mutable pour compliquer davantage les choses.

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

Que se passe-t-il lorsqu'une tentative d'écriture en mémoire réellement en lecture seule (ROM, par exemple) n'est probablement pas définie dans la norme?

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