Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Compilateur trop pointilleux sur des "const" ?

2 réponses
Avatar
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 ("\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 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.

2 réponses

Avatar
Jean-Marc Bourguet
PIGUET Bruno writes:

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
Avatar
-ed-
On 17 avr, 15:42, PIGUET Bruno wrote:
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 ("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 <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 ("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 <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 ("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.