Compilateur trop pointilleux sur des "const" ?

Le
PIGUET Bruno
Bonjour à tous,


Soit l'ECM suivant :
--debut_citation-
#include <stdio.h>

void
f_cst (const char *const content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
fputs (content[i], stdout);
fputs ("", stdout);
}
}

void
f_var (char * content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
content[i] = "zz";
}
}

int
main ()
{
const char *tab1[] = { "a", "b", "c" };
char *tab2[] = { "1", "2", "3" };

/* cas normal */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));
/* Le compilateur va râler. Mais où est le danger ? */
f_cst (tab2, sizeof (tab2) / sizeof (char *));
/* un cast permet de rassurer le compilateur */
f_cst ((const char * const *)tab2, sizeof (tab2) / sizeof (char *));

/* cas normal */
f_var (tab2, sizeof (tab2) / sizeof (char *));
/* Le compilateur va râler. Il a raison : cette fonction va modifier
tab1 */
f_var (tab1, sizeof (tab1) / sizeof (char *));
/* un cast permet de passer outre : gare au résultat */
f_var ((char **)tab1, sizeof (tab2) / sizeof (char *));

/* Pour vérifier que tab1 a bien été modifié */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));

return 0;
}
--fin_citation-

Je le compile avec gcc (4.2.3) :
gcc -ansi -Wall -Wextra essai_const_char_arg.c -o essai_const_char_arg

J'ai les messages d'erreurs annoncés par les commentaires des lignes
32 et 39 :
essai_const_char_arg.c:33: attention : passing argument 1 of ‘f_cst’ from
incompatible pointer type

Je ne comprend pas pourquoi j'ai le même message d'erreur sur ces 2
lignes.
En ligne 33, je passe un simple "char *tab2[]" à une fonction qui
travaille sur un "const char *const content[]"
J'ai tendance à penser qu'il n'y a pas gros risque : la fonction ne va
rien modifier dans mon tableau.

En ligne 40, c'est l'inverse : je passe un "const char *tab1[]" à
une fonction qui travaille sur un "char * content[]" et qui
va le modifier. Je perd donc le caractère "const", il est normal que
le compilateur râle.

Dans le cas "pas dangereux", je peux faire taire le compilateur par un
cas (ligne 35). Mais je n'aime pas bien ça, car le même genre de technique
utilisé ligne 42 peut avoir beaucoup plus de conséquences fâcheuses.

Pour le dire autrement : dans un "vrai" code, il ne faut pas faire le
cast de la ligne 42, et je n'aimerais donc pas être obligé de faire le
cast de la ligne 35, qui y ressemble trop.

J'ai sans doute loupé qqchose. Quel peut être le risque qui justifie le
message d'erreur de la ligne 33 ?
Ou alors quelle est la "bonne" syntaxe pour indiquer qu'une fonction ne
va rien modifier dans le tableau de chaînes de caractères qu'on
lui passe en argument ?

Merci,

Bruno.
PS :
Ajouter "-Wno-write-strings " sur la ligne de compilation ne change rien.
Questions / Réponses high-tech
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Jean-Marc Bourguet
Le #19135781
PIGUET Bruno
J'ai sans doute loupé qqchose. Quel peut être le risque qui justifie le
message d'erreur de la ligne 33 ?



J'ai pas le temps de regarder en detail si c'est bien le cas ici, mais avec
des pointeurs vers const, le C interdit des choses qui sont sures. Le C++
a des regles plus compliquees a ce sujet et il me semble qu'il autorise
tout ce qui est sur et uniquement cela.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
-ed-
Le #19139361
On 17 avr, 15:42, PIGUET Bruno
Bonjour à tous,

  Soit l'ECM suivant :
-----debut_citation----------
#include
void
f_cst (const char *const content[], size_t nb)
{
  size_t i;
  for (i = 0; i < nb; i++)
    {
      fputs (content[i], stdout);
      fputs ("n", stdout);
    }

}

void
f_var (char * content[], size_t nb)
{
  size_t i;
  for (i = 0; i < nb; i++)
    {
      content[i] = "zz";
    }

}

int
main ()
{
  const char *tab1[] = { "a", "b", "c" };
  char *tab2[] = { "1", "2", "3" };

  /* cas normal */
  f_cst (tab1, sizeof (tab1) / sizeof (const char *));
  /* Le compilateur va râler. Mais où est le danger ? */
  f_cst (tab2, sizeof (tab2) / sizeof (char *));
  /* un cast permet de rassurer le compilateur */
  f_cst ((const char * const *)tab2, sizeof (tab2) / sizeof (char *));

  /* cas normal */
  f_var (tab2, sizeof (tab2) / sizeof (char *));
  /* Le compilateur va râler. Il a raison  : cette fonction va modi fier
tab1 */
  f_var (tab1, sizeof (tab1) / sizeof (char *));
  /* un cast permet de passer outre : gare au résultat  */
  f_var ((char **)tab1, sizeof (tab2) / sizeof (char *));

  /* Pour vérifier que tab1 a bien été modifié */
  f_cst (tab1, sizeof (tab1) / sizeof (const char *));

  return 0;}

-----fin_citation----------

Je le compile avec gcc (4.2.3) :
gcc -ansi -Wall -Wextra  essai_const_char_arg.c   -o essai_const_char _arg

J'ai les messages d'erreurs annoncés par les commentaires des lignes
32 et 39 :
essai_const_char_arg.c:33: attention : passing argument 1 of ‘f_cst’ from
incompatible pointer type

Je ne comprend pas pourquoi j'ai le même message d'erreur sur ces 2
lignes.
En ligne 33, je passe un simple "char *tab2[]" à une fonction qui
travaille sur un "const char *const content[]"
J'ai tendance à penser qu'il n'y a pas gros risque : la fonction ne va
rien modifier dans mon tableau.

En ligne 40, c'est l'inverse : je passe un "const char *tab1[]" à
une fonction qui travaille sur un "char * content[]" ... et qui
va le modifier. Je perd donc le caractère "const", il est normal que
le compilateur râle.

Dans le cas "pas dangereux", je peux faire taire le compilateur par un
cas (ligne 35). Mais je n'aime pas bien ça, car le même genre de tech nique
utilisé ligne 42 peut avoir beaucoup plus de conséquences fâcheuses .

Pour le dire autrement : dans un "vrai" code, il ne faut pas faire le
cast de la ligne 42, et je n'aimerais donc pas être obligé de faire l e
cast de la ligne 35, qui y ressemble trop.

J'ai sans doute loupé qqchose. Quel peut être le risque qui justifie le
message d'erreur de la ligne 33 ?
Ou alors quelle est la "bonne" syntaxe pour indiquer qu'une fonction ne
va rien modifier dans le tableau de chaînes de caractères qu'on
lui passe en argument ?


Mon compilateur indique (j'ai remis les messages dans le contexte)

#include
void
f_cst (const char *const content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
fputs (content[i], stdout);
fputs ("n", stdout);
}

}

void
f_var (char * content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
content[i] = "zz"; /* C:devhellomain.c:21: warning:
assignment discards qualifiers from pointer target type */
}

}

int
main (void)
{
const char *tab1[] = { "a", "b", "c" };
char *tab2[] = { "1", "2", "3" }; /* C:devhellomain.c:30:
warning: initialization discards qualifiers from pointer target type
*/

/* cas normal */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));
/* Le compilateur va râler. Mais où est le danger ? */
f_cst (tab2, sizeof (tab2) / sizeof (char *)); /* C:devhello
main.c:35: warning: passing arg 1 of `f_cst' from incompatible
pointer type */
/* un cast permet de rassurer le compilateur */
f_cst ((const char * const *)tab2, sizeof (tab2) / sizeof (char *));

/* cas normal */
f_var (tab2, sizeof (tab2) / sizeof (char *));
/* Le compilateur va râler. Il a raison : cette fonction va
modifier
tab1 */
f_var (tab1, sizeof (tab1) / sizeof (char *)); /* C:devhello
main.c:43: warning: passing arg 1 of `f_var' from incompatible
pointer type */
/* un cast permet de passer outre : gare au résultat */
f_var ((char **)tab1, sizeof (tab2) / sizeof (char *));

/* Pour vérifier que tab1 a bien été modifié */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));

return 0;
}

ce qui est tout à fait justifié. Il faut savoir qu'une chaine n'est
pas modifiable et qu'il est donc recommandé de la définir 'const'.

Ce code compile sans erreurs :

Aucun cast n'est nécessaire. Le fait de 'rassurer' le compilateur
masque le défaut (potentiel), mais ne le corrige pas.

#include
void
f_cst (const char *const content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
fputs (content[i], stdout);
fputs ("n", stdout);
}

}

void
f_var (char const * content[], size_t nb)
{
size_t i;
for (i = 0; i < nb; i++)
{
content[i] = "zz";
}

}

int
main (void)
{
const char *tab1[] = { "a", "b", "c" };
char const *tab2[] = { "1", "2", "3" };

/* cas normal */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));
/* Le compilateur va râler. Mais où est le danger ? */
f_cst (tab2, sizeof (tab2) / sizeof (char *));

/* cas normal */
f_var (tab2, sizeof (tab2) / sizeof (char *));
/* Le compilateur va râler. Il a raison : cette fonction va
modifier
tab1 */
f_var (tab1, sizeof (tab1) / sizeof (char *));

/* Pour vérifier que tab1 a bien été modifié */
f_cst (tab1, sizeof (tab1) / sizeof (const char *));

return 0;
}

Sortie :
a
b
c
1
2
3
zz
zz
zz

Process returned 0 (0x0) execution time : 0.049 s
Press any key to continue.
Publicité
Poster une réponse
Anonyme