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

Warning de g++ pour redéfinition de variable ?

17 réponses
Avatar
Rémi Moyen
Bonjour,

Dans mon code C++, il m'arrive de temps =E0 autres de faire une erreur
b=EAte de copier-coller et d'avoir (typiquement) deux boucles imbriqu=E9es
qui utilisent involontairement le m=EAme indice (par exemple parce que
j'ai copi=E9 la boucle interne d'un autre endroit du code et que j'ai
oubli=E9 de changer son indice), genre :

for (int i =3D 0; i < n; ++i) {
...
for (int i =3D 0; i < m; ++i) { // Oops, it's supposed to be j
here !
...
}
}

Je me dis que g++ devrait =EAtre capable de m'avertir de ce genre
d'erreurs, parce que =E7a me semble quand m=EAme =EAtre une construction qui=

doit =EAtre une erreur du programmeur dans la tr=E8s grande majorit=E9 des
cas, et que =E7a ne me semble pas tr=E8s difficile =E0 d=E9tecter.

Mais j'ai cherch=E9 rapidement (peut-=EAtre trop...) dans le man de g++,
sans trouver de flag qui permette de forcer un warning dans des cas
comme =E7a. J'ai mal cherch=E9 ou =E7a existe pas ?

Merci d'avance !
--
R=E9mi Moyen

7 réponses

1 2
Avatar
Paul Gaborit
À (at) Fri, 4 Apr 2008 06:08:22 -0700 (PDT),
Rémi Moyen écrivait (wrote):
Paul Gaborit wrote:
Pour les autres, c'est au cas par cas.


Ben justement, c'est un peu ça qui m'intéresse. La liste est tellement
longue, et parfois un peu difficile à comprendre que j'aimerais bien
savoir ceux qui, dans l'experience des gens sont :
- utiles ;
- et ne génèrent pas trop de faux positif.

Évidemment, je conçois bien que ce soit au cas par cas, certains
projets/styles/équipes/... n'auront pas les même choix, ou que
certains warnings soient vraiment spécifique. Mais par exemple, le -
Wshadow, j'ai un peu du mal à imaginer des situations où ce soit un
choix volontaire et véritablement logique de redéfinir une variable
(le seul exemple que je trouve est avec des macros, mais c'est de
toute façon du code sale à mon avis). Je suppose qu'il y en a d'autres
(en plus de -Wall) qui sont sans doute utilisables "tout le temps par
défaut sauf quand un cas particulier justifie qu'on ne les mette pas".

Enfin, c'est ma vision des choses, je me plante peut-être...


Ça me semble aussi une bonne vision des choses !

Mais, là, mon avis ne te sera d'aucune utilité car les options de gcc
évoluent assez vite et je ne développe quasiment plus en C/C++ sauf
pour de l'initiation ou du debug ponctuel...

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/>


Avatar
Paul Gaborit
À (at) Fri, 04 Apr 2008 15:40:13 +0200,
Alain Ketterlin écrivait (wrote):
Au fait, avec le code suivant (non, il ne me semble pas idiot) :

for ( int i=0 ; i<10 ; i++ )
for ( int i=i+1 ; i<10 ; i++ )
printf("%dn",i);

Ca part dans le décor. A mon avis ca ne devrait pas. Le problème vient
de l'initalisation (int i=i+1;) dont le i en rhs n'est pas le bon :
c'est "le nouveau" qui est pris, pas "l'ancien".


À mon avis, c'est clairement un bug de g++ puisque les deux extraits
suivants fonctionnent :

for ( int i=0 ; i<10 ; i++ ) {
int i = i + 1;
for (int j = i ; j<10 ; j++ ) {
printf("%dn",j);
}
}

ou :

for ( int i=0 ; i<10 ; i++ ) {
int j = i + 1;
for (int i = j ; i<10 ; i++ ) {
printf("%dn",i);
}
}


Ici, j'utilise la version 4.2.1 de gcc/g++.

Dommage. Dans ce cas, il vaudrait mieux que -Wshadow soit dans
-Wall.


Mais avec le bug, ce n'est plus un warning mais carrément une erreur !

P/S: je vois des adeptes de la programmation fonctionnelle qui
rigolent...


C'est sûr.

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/>

Avatar
Rémi Moyen
On Apr 4, 2:40 pm, Alain Ketterlin
wrote:
Paul Gaborit writes:

Formellement, il n'est ni interdit ni complètement idiot de réutilis er
plusieurs fois le même nom de variable. Ça peut même arriver sans
faire de "bête" copier/coller : par exemple via une macro.



Oui, ça c'est l'exemple auquel j'avais pensé et où ça me semble
justifié. Ceci dit, d'un point de vue "propreté du code", ça me para ît
quand même limite, parce qu'il faut être sûr que la variable en
question ne sera pas passée dans les arguments de la macro (si elle en
a), ce qui n'est faisable que si on sait ce qui est dans la macro, ce
qui en limite un peu l'intérêt. Bon, si la macro n'a pas d'arguments,
ça doit tenir la route.

Enfin, je préfère éviter les macros, surtout quand elles sont
suffisamment compliquées (et une qui contient des variables locales
l'est sans doute). Mais c'est mon avis, chacun fait ce qu'il veut --
et je code en C++, pas en C, ça aide aussi.

C'est donc
normal que 'gcc' ne considère pas cela comme une erreur ni même un
warning. Ensuite, on peut considérer que ce n'est pas une bonne
pratique. D'où l'option -Wshadow...


Tout à fait d'accord.

Au fait, avec le code suivant (non, il ne me semble pas idiot) :


Ben j'ai quand même du mal à voir en quoi le fait de redéfinir la mê me
variable soit vraiment ce que tu veux. Si tu remplaces le deuxième 'i'
par 'j', OK, ça ne me pose aucun problème, c'est un bout de code tout
à fait logique. Mais quelle situation, quel problème à résoudre
justifie que tu veuilles absolument réutiliser la même variable
(enfin, le même nom) ? À part pour dire "tous mes indices de boucles
s'appellent i", pourquoi un programmeur se dirait "je vais définir une
nouvelle variable, mais elle *doit* avoir le même nom" ?

C'est une vraie question, je cherche à trouver dans quel contexte
cette fonctionnalité est utile (et non pas seulement autorisée). Pour
l'instant, je n'ai vu que les macros...
--
Rémi Moyen


Avatar
Matthieu Moy
Paul Gaborit writes:

À (at) Fri, 04 Apr 2008 15:40:13 +0200,
Alain Ketterlin écrivait (wrote):
Au fait, avec le code suivant (non, il ne me semble pas idiot) :

for ( int i=0 ; i<10 ; i++ )
for ( int i=i+1 ; i<10 ; i++ )
printf("%dn",i);

Ca part dans le décor. A mon avis ca ne devrait pas. Le problème vient
de l'initalisation (int i=i+1;) dont le i en rhs n'est pas le bon :
c'est "le nouveau" qui est pris, pas "l'ancien".


À mon avis, c'est clairement un bug de g++ puisque les deux extraits
suivants fonctionnent :


Un bug de g++, je crois pas. Une bizarerie de C++, oui.

for ( int i=0 ; i<10 ; i++ ) {
int i = i + 1;
for (int j = i ; j<10 ; j++ ) {
printf("%dn",j);
}
}


Marche, mais regardes ce qu'en dit valgrind ;-) (uninitialized value
par ci, uninitialized value par là, ...).

Autre expérience intéressante :

for ( int i=0 ; i<10 ; i++ ) {
int toto = toto + 1;
for (int j = toto ; j<10 ; j++ ) {
printf("%dn",j);
}
}

Ça compile sans warning avec g++ -Wall -Wextra -Wshadow, et ça marche
tout aussi bien que ta version (enfin, avec gcc 3.4, pas avec 4.2 qui
ne fait marcher ni ma version ni la tienne, mais quand on manipule des
variables pas initialisées, faut s'attendre à tout).

En C++, dans une initialisation, la variable en train d'être
initialisée existe déjà du point de vue vérifications de typage, et à
l'exécution, on prends ce qui est à cet endroit là dans la mémoire où
le registre, c'est à dire n'importe quoi.

Ça permet de faire des choses du genre

struct toto x = init_toto(&x);

par exemple.

--
Matthieu


Avatar
Olivier Miakinen

[Oups, y'en a un de nous deux qui a un problème avec les accents...


Pourtant, aussi bien son article que le tien définissent correctement
le jeu de caractères utilisé, mais le tien est en UTF-8 et en quoted-
printable, et tous les caractères accentués de Paul ont été transformés
en le caractère U+FFFD (CARACTÈRE DE REMPLACEMENT). Note que le titre
de l'article, encodé en Latin1 chez Paul, a été décodé puis réencodé,
toujours en Latin1, chez toi (avec le bon é).

j'avoue, j'utilise Google Groups depuis le bureau, c'est donc peut-
être bien moi.]


Disons que c'est probablement Google groupes le problème.

--
Rémi Moyen


Le délimiteur de signature est incorrect, lui aussi.

Avatar
Paul Gaborit
À (at) Fri, 04 Apr 2008 17:42:10 +0200,
Matthieu Moy écrivait (wrote):
Autre expérience intéressante :

for ( int i=0 ; i<10 ; i++ ) {
int toto = toto + 1;
for (int j = toto ; j<10 ; j++ ) {
printf("%dn",j);
}
}

Ça compile sans warning avec g++ -Wall -Wextra -Wshadow, et ça marche
tout aussi bien que ta version (enfin, avec gcc 3.4, pas avec 4.2 qui
ne fait marcher ni ma version ni la tienne, mais quand on manipule des
variables pas initialisées, faut s'attendre à tout).



int toto = toto + 10;

ou même (en C++) :

int toto(toto + 10);

Qu'une variable 'toto' existe ou non dans la portée courante ne change
rien. Le deuxième 'toto' fait bien appel à la valeur du premier qui
est évidemment non initialisée et donc le résultat est imprévisible.

Ce qui m'étonne, c'est que g++ ne dise rien sur ce genre de
construction...

Comme d'habitude, il faudrait lire ce qu'en dit la norme.

En C++, dans une initialisation, la variable en train d'être
initialisée existe déjà du point de vue vérifications de typage, et à
l'exécution, on prends ce qui est à cet endroit là dans la mémoire où
le registre, c'est à dire n'importe quoi.

Ça permet de faire des choses du genre

struct toto x = init_toto(&x);


Oui. Ma trop grande pratique de Perl m'a fait oublié mes réflexes de
C/C++ ! ;-)

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/>

Avatar
Rémi Moyen
Olivier Miakinen wrote:

Disons que c'est probablement Google groupes le problème.


Oui, c'est ce que je sous-entendais.

--
Rémi Moyen


Le délimiteur de signature est incorrect, lui aussi.


Tiens, oui. Et c'est comme ça dans tous mes messages, Google doit
supprimer tous les espaces en fin de ligne. Zut. Y'a-t-il un moyen
facile pour faire une signature propre ?

Rémi Moyen
(c'est moins propre, mais ça fait pas semblant de l'être, au
moins ;-) )


1 2