char const * const argv[] vs char const *argv[] vs char* argv[]
Le
Marc Boyer
Je crossposte sur les deux forums C et C++ car le code est
semble dans l'intersection dans languages, meme si la semantique
est peut-etre differente. Que chacun reponde dans le(s) forum(s)
appropries a sa reponse.
Un warning de mon compilateur (gcc 3.3) me fait douter de ma
comprehension de const
Si je passe un pointeur sur des char a une fonction qui attend
un pointeur sur des char constant, tout va bien (gcc et g++).
Mais quand j'attaque les pointeurs de pointeur (manip de argv),
ca se corse.
Ni gcc ni g++ n'acceptent la conversion char* argv[] en const char* argv[].
Je comprends qu'avec un pointeur de pointeur de char, on peut modifier
l'argument, dans le sens ou on fait pointeur sur "autre chose", mais bon,
si je veux interdire une modif de l'argument, j'ecris
char const * const argv[]
donc deja, je comprends pas trop
Ensuite, quand je tente la signature char const * const argv[], gcc
continue a raler, mais g++ ne dit plus rien.
Voici un code illustratif de mon incomprehension
void foo(const char* string);
void barConst(char const * argv[]);
void barConstConst(char const * const argv[]);
int main(int argc, char* argv[]){
char aString[10];
aString[0]= 0;
foo(aString); // OK pour gcc et g++
barConst(argv); // gcc "type pointeur incompatible"
// g++ "conversion invalide de"
barConstConst(argv); // gcc "type pointeur incompatible"
// g++ OK
return 0;
}
Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens
semble dans l'intersection dans languages, meme si la semantique
est peut-etre differente. Que chacun reponde dans le(s) forum(s)
appropries a sa reponse.
Un warning de mon compilateur (gcc 3.3) me fait douter de ma
comprehension de const
Si je passe un pointeur sur des char a une fonction qui attend
un pointeur sur des char constant, tout va bien (gcc et g++).
Mais quand j'attaque les pointeurs de pointeur (manip de argv),
ca se corse.
Ni gcc ni g++ n'acceptent la conversion char* argv[] en const char* argv[].
Je comprends qu'avec un pointeur de pointeur de char, on peut modifier
l'argument, dans le sens ou on fait pointeur sur "autre chose", mais bon,
si je veux interdire une modif de l'argument, j'ecris
char const * const argv[]
donc deja, je comprends pas trop
Ensuite, quand je tente la signature char const * const argv[], gcc
continue a raler, mais g++ ne dit plus rien.
Voici un code illustratif de mon incomprehension
void foo(const char* string);
void barConst(char const * argv[]);
void barConstConst(char const * const argv[]);
int main(int argc, char* argv[]){
char aString[10];
aString[0]= 0;
foo(aString); // OK pour gcc et g++
barConst(argv); // gcc "type pointeur incompatible"
// g++ "conversion invalide de"
barConstConst(argv); // gcc "type pointeur incompatible"
// g++ OK
return 0;
}
Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens

Poser une question


La déclaration "correcte" est
char * const argv[]
<DIGRESSION>
Pour des raisons de compatibilité avec l'existant, l'officielle n'a pas le
const. Mais la norme C dans son texte refuse l'autorisation de modifier les
éléments du tableau: 5.1.2.2.1p2, en partie:
-- The parameters *argc* and *argv* and the strings pointed to by the
*argv* array shall be modifiable by the program, and retain their last-
stored values between program startup and program termination.
Par élimination, tout est modifiable, sauf les éléments du tableau argv
original. Autrement sit, si tu veux modifier les éléments de argv[], il faut
d'arbord copier le tableau ailleurs, faire les modifications que tu veux, et
ensuite réaffecter argv vers ta copie locale.
</DIGRESSION>
GCC C (et probablement GCC C++), dans son infinie grandeur, te laisse sans
broncher faire des conversions de argv vers un char *[], pour la même raison
que la norme: compatibilité avec l'existant qui ne coannaitr pas les
bienfaits de const. Mais à partir du moment où il y a un const dans le type,
GCC C devient féroce, et refuse de convertir un tableau non modifiable
(argv) en tableau modifiable (ton paramètre), même si dans le même temps tu
augmentes la contrainte sur les éléments pointés (modifiables selon la
norme, non modifiés selon ton prototype).
D'autre part, il y a la tentative d'augmenter la contrainte sur les chaînes
pointées. Ici, je pense que C et C++ divergent, donc tout ce que j'écris
peut ne pas être vrai en C++.
En C, donc, la règle sur const signifiant "ne sera pas modifié" s'applique
uniquement au paramètre lui-même, pas aux composants à partir desquels est
construit son type.
Détail: 6.3.2.2/6.5.2.2 Function calls, par. 2, en partie:
Each argument shall have a type such that its value may be assigned to
an object with the unqualified version of the type of its corresponding
parameter.
Ce qui renvoie vers les affectations: 6.3.16.1/6.5.16.1 Simple assignment,
par.1, en partie:
- both operands are pointers to qualified or unqualified versions of
compatible types, and the type pointed to by the left has all the
qualifiers of the type pointed to by the right;
Il faut donc s'en remettre aux règles de compatibilité de types pour voir si
on peut ajouter des qualificateurs sur les sous-éléments. Et là, surprise,
si tu changes les qualificateurs, ce n'est plus compatible: 6.5.4.1/6.7.5.1
Pointer declarators, par.2:
For two pointer types to be compatible, both shall be identically
qualified and both shall be pointers to compatible types.
Ce qui explique les râleries de GCC C.
Antoine
argc/argv pour l'illustrer ou bien les parametres de main sont parties
du problemes?). Si c'est simplement comme illustration du probleme de
conversion entre types pointeurs de pointeurs avec des qualifications
const/volatile differentes:
Les regles C++ telles que je m'en souviens sont:
- si a un niveau, la source a un const, alors la destination doit
l'avoir aussi; de meme pour volatile
- si a un niveau, la destination a un const et la source pas, alors
la destination doit avoir un const a tous les niveaux precedants
Exemple de ce que la deuxieme regle veut eviter:
int main() {
char const c = 'c';
char* pc;
char const** pcc = &pc; // interdit
*pcc = &c;
*pc = 'C'; // modifie c
}
(suivi sur fr.comp.lang.c++, je ne traite que du C++ et ne sais pas si
les regles sont differentes en C)
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++...index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Dans le cas qui motive mon questionnement, je reproduis les memes
messages d'erreur en subsituant toto a main dans le code.
Mais je saute quand meme sur la digression.
En fait, dans l'usage immediat que j'en vois, ca permet de modifier l'ordre
des parametres, pour, par exemple, mettre les options au debut de la ligne
de commande et se faciliter l'analyse).
Sauf que meme si j'ecris
int toto(int argc, char* argv[])
c'est pareil ;-0
Je sens en effet des divergences (ce qui m'a pousse a un crosspost,
m'obligeant a forcer la main a mon lecteur de news)
La, je seche... Prenons
void foo(const char* s)
qu'est-ce que le parametre ( s ?) et son type c'est char* ou const char* ?
unqualified == pas de const ?
si le type c'est char const * const, le unqualified, c'est
char const * ou char* ?
Plus je relis les paragraohes que tu cites, plus ca me semble
crucial dans la comprehension.
Heuh... J'ai pas tout saisi la...
Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...
Je ne comprends plus ce que tu ne comprends pas ;-)
Il n'y pas de conversion automatique char** vers const char** parce
que la norme le dit ;-) On en a déjà parlé ici plusieurs fois.
Un exemple qui montre qu'une conversion automatique ne serait pas sure
const char *cc = "hello world!";
void foo(const char **pp)
{
*pp = cc;
}
char *p;
foo(&p); /* si c'était légal on pourrait */
p[0]="H"; /* dévier un char* sur une chaîne constante. */
...oops.....
--
Horst
serait intelligent. Et ensuite je me suis aperçu que le problème était plus
de fond, en rien spécifique à argv. J'ai laissé la digression, que je
trouvais intéressante (chacun ses goûts ;-)), mais cela a dû t'égarer plus
qu'autre chose. Désolé.
En c6627l$bu7$, Marc Boyer va escriure:
Voui. Et pas le droit de faire cela salement directement dans argv (genre
appeler qsort).
Attention, il y a trois niveaux (ou plus).
Le premier const (à partir de la gauche, donc à droite du *), saute
automatiquement, puisqu'il faut bien passer l'argument (il s'applique dans
la définition de la fonction). C'est le 6.3.2.2/6.5.2.2 ci-dessous.
Le deuxième const saute en vertu d'une règle spéciale, destinée si je me
rappelle bien à faciliter la compatibilité et l'utilisabilité de const, en
particulier la croyance que « const * pour un paramètre signifie que la
fonction ne modifie pas l'argument », une utile manière de voir les choses
mais la norme n'est pas formulée de cette manière.
Les autres const... ne sautent pas ! Ils restent ! Et ils bloquent.
Oui.
const char *
Donc ici, on n'a que deux niveaux, ce n'est pas « intéressant ». ;-)
Oui, mais uniquement pour le type du paramètre, _pas_ pour le type vers
lequel ce type pointe.
char const *
Et dans tes exemples initiaux,
void barConst(char const * argv[]);
le type de argv, c'est char const * * (le type tableau est réécrit en
pointeur).
void barConstConst(char const * const argv[]);
Ici, le type de argv, c'est char const * const *.
C'est là qu'est le "truc". En vertu de cette règle, le const du s de ton
exemple foo « saute ». Mais pas celui de argv (dans les deux cas), c'est
char const * *. Alors que argv et toto, je le rappelle, sont de type char *
[], qui passe à char * * pour l'affectation.
Ça va mieux, maintenant ?
Antoine