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

Valeur d'une variable locale non-initialisée

28 réponses
Avatar
Rémi Moyen
Bonjour,

En relisant un bout de code, je suis tomb=E9 sur une classe qui contient
un int comme membre, et bien que cette variable ne soit jamais
initialis=E9e explicitement, elle a toujours la valeur 0 et le code
marche correctement. Ce que je ne comprends pas vraiment, je croyais
que les variables locales n'=E9taient pas initialis=E9es =E0 0
automatiquement...

Si je regarde mon Stroustrup, il me dit (4.9.5) que seules les
variables globales ou statiques locales sont initialis=E9es =E0 0 par
d=E9faut, et qu'une variable non-initialis=E9e locale ou cr=E9=E9e sur le t=
as
a une valeur non-d=E9finie. Les variables membres d'une classe, dans
quelle cat=E9gorie tombent-elles ? J'aurais dit le deuxi=E8me cas, non ?

Bon, je teste avec un petit code tout simple :
#include <iostream>
using namespace std;

int v1; //global, non static var
static int v2; //global, static var

void f() {
int v3; //local, non static
static int v4; //local, static
cout << "f(): " << v3 << " " << v4 << endl;
}

class A {
public:
void f() {cout << "A::f(): " << v5_ << " " << v6_ << endl;}
private:
int v5_; // class, non static
static int v6_; // class, static
};
int A::v6_;

int main() {
cout << "main(): " << v1 << " " << v2 << endl;
f();
A a;
a.f();
return 0;
}

Je compile avec :
g++ -ovariable -O1 -Wuninitialized main.cpp
main.cpp: In function `void f()':
main.cpp:12: warning: 'v3' might be used uninitialized in this
function

Et j'execute :
main(): 0 0
f(): 0 0
A::f(): 0 0

Donc clairement, toutes les variables ont =E9t=E9 mises =E0 0. Pour v1 et
v2, c'est normal. v4 et v6_ aussi, elles sont statiques. Mais pour v3
et v5_ ? g++ m'informe bien pour v3, mais pourquoi ne m'informe-t-il
pas aussi pour v5_ ? Est-ce qu'elles sont =E0 0 par hasard (apr=E8s tout,
une valeur "non-d=E9finie" par le standard peut tr=E8s bien =EAtre 0) ? Est=
-
ce une particularit=E9 de g++ (v3.4.5, sur Linux RHEL 4.3) ? Est-ce
normal et j'ai mal compris le bouquin ?

Si quelqu'un pouvait m'expliquer, =E7a m'aiderait pas mal... Merci
d'avance !
--
R=E9mi Moyen

10 réponses

1 2 3
Avatar
Jean-Marc Bourguet
Rémi Moyen writes:

Donc clairement, toutes les variables ont été mises à 0. Pour v1 et
v2, c'est normal. v4 et v6_ aussi, elles sont statiques. Mais pour v3
et v5_ ? g++ m'informe bien pour v3, mais pourquoi ne m'informe-t-il
pas aussi pour v5_ ? Est-ce qu'elles sont à 0 par hasard



Pas tout a fait par hasard (les OS initialisent toute la memoire a 0 avant
de la donner a un process), mais tu ne peux pas compter dessus. En
modifiant legerement ton programme (en faisant en sorte que la classe soit
allouee a un endroit ou on a deja ecrit d'autres valeurs et en faisant en
sorte que la pile soit de meme modifiee), j'arrive a d'autres valeurs.

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++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Fabien LE LEZ
On Mon, 6 Jul 2009 02:12:00 -0700 (PDT), Rémi Moyen
:

main(): 0 0
f(): 0 0
A::f(): 0 0



VC++ 2008 :
main(): 0 0
f(): 4331500 0
A::f(): 1 0

Donc clairement, toutes les variables ont été mises à 0.



Si le standard n'indique pas de valeur par défaut, l'implémentation
est libre d'assigner la valeur qu'elle veut. Ça peut être 0, ou la
suite d'octets qui se trouvait là en mémoire, ou 42.

Et même quand la norme indique que l'initialisation se fera, il me
semble qu'il vaut tout de même mieux l'expliciter si tu en as besoin.
Avatar
Rémi Moyen
On 6 juil, 10:21, Jean-Marc Bourguet wrote:
Rémi Moyen writes:
> Donc clairement, toutes les variables ont été mises à 0. Pour v1 et
> v2, c'est normal. v4 et v6_ aussi, elles sont statiques. Mais pour v3
> et v5_ ? g++ m'informe bien pour v3, mais pourquoi ne m'informe-t-il
> pas aussi pour v5_ ? Est-ce qu'elles sont à 0 par hasard

Pas tout a fait par hasard (les OS initialisent toute la memoire a 0 avan t
de la donner a un process), mais tu ne peux pas compter dessus.



D'accord. C'est à peu près ce que je soupçonnais, merci de confirmer.

Cependant, j'ai encore un doute avec le warning de g++ : pourquoi il
ne m'a pas prévenu pour A::v5_ ?

Est-ce que c'est parce qu'il n'arrive pas à tracer de manière fiable
tout le code et qu'au moment où j'appelle A::f(), il est trop
difficile pour lui de savoir si la variable a été initialisée ou non,
ou est-ce parce que quelque chose dans le langage garantit que la
variable est initialisée à 0 ?

 En
modifiant legerement ton programme (en faisant en sorte que la classe soi t
allouee a un endroit ou on a deja ecrit d'autres valeurs et en faisant en
sorte que la pile soit de meme modifiee), j'arrive a d'autres valeurs.



Par curiosité et parce que j'aimerais bien le faire moi-même, tu peux
me donner le programme modifié ?

Merci de ta réponse rapide !
--
Rémi Moyen
Avatar
Rémi Moyen
On 6 juil, 10:26, Fabien LE LEZ wrote:

[snip l'exemple, merci de confirmer que les 0 dans v3 et A::v5_ ne
sont pas imposés]

Si le standard n'indique pas de valeur par défaut, l'implémentation
est libre d'assigner la valeur qu'elle veut. Ça peut être 0, ou la
suite d'octets qui se trouvait là en mémoire, ou 42.

Et même quand la norme indique que l'initialisation se fera, il me
semble qu'il vaut tout de même mieux l'expliciter si tu en as besoin.



Je suis d'accord. Disons que je cherche essentiellement à comprendre
pourquoi le code fautif que j'ai trouvé marchait quand même (i.e.
pourquoi c'était toujours mis à 0). Je vois 3 raisons possibles :
1) le standard dit que ça doit être 0 (ta réponse et celle de Jean-
Marc excluent clairement cela, ce que je soupçonnais) ;
2) l'OS ou le compilateur mettent toujours systématiquement à 0, c'est
un choix d'implémentation (idem, c'est pas comme ça que ça marche) ;
3) dans mon cas particulier, un "coup de chance" (le fait que mon
programme de test est simpliste et utilise un bout de mémoire vierge,
par exemple) fait que l'OS a tout mis à 0 avant. Et c'est bien ça qui
se passe, je crois.

Vu que c'est dans du code que je maintiens, le cas 3 veut dire qu'il
faut impérativement que je passe du temps rapidement à vérifier si
cette erreur n'est pas faite ailleurs qu'à l'endroit où je l'ai vue
alors que les cas 1 et 2, j'aurais pu me permettre de ne les corriger
que quand je tombe dessus par hasard en cherchant autre chose, parce
que même si ça n'est pas du code très propre à mon goût, ça mar che
quand même comme attendu.

Merci pour ta réponse, je crois que j'ai toutes les infos
nécessaires !
--
Rémi Moyen
Avatar
Jean-Marc Bourguet
Rémi Moyen writes:

On 6 juil, 10:21, Jean-Marc Bourguet wrote:
> Rémi Moyen writes:
> > Donc clairement, toutes les variables ont été mises à 0. Pour v1 et
> > v2, c'est normal. v4 et v6_ aussi, elles sont statiques. Mais pour v3
> > et v5_ ? g++ m'informe bien pour v3, mais pourquoi ne m'informe-t-il
> > pas aussi pour v5_ ? Est-ce qu'elles sont à 0 par hasard
>
> Pas tout a fait par hasard (les OS initialisent toute la memoire a 0 avant
> de la donner a un process), mais tu ne peux pas compter dessus.

D'accord. C'est à peu près ce que je soupçonnais, merci de confirmer.

Cependant, j'ai encore un doute avec le warning de g++ : pourquoi il
ne m'a pas prévenu pour A::v5_ ?

Est-ce que c'est parce qu'il n'arrive pas à tracer de manière fiable
tout le code et qu'au moment où j'appelle A::f(), il est trop
difficile pour lui de savoir si la variable a été initialisée ou non,



Vraisemblablement quelque chose du genre.

ou est-ce parce que quelque chose dans le langage garantit que la
variable est initialisée à 0 ?

> En
> modifiant legerement ton programme (en faisant en sorte que la classe soit
> allouee a un endroit ou on a deja ecrit d'autres valeurs et en faisant en
> sorte que la pile soit de meme modifiee), j'arrive a d'autres valeurs.

Par curiosité et parce que j'aimerais bien le faire moi-même, tu peux
me donner le programme modifié ?



Voici (remarque l'ajout du dummy[1] qui a ete necessaire, j'ai pas ete voir
l'assembleur genere pour comprendre pourquoi).

#include <iostream>
using namespace std;

int v1; //global, non static var
static int v2; //global, static var

void f() {
int dummy[1];

int v3; //local, non static
static int v4; //local, static
cout << "f(): " << v3 << " " << v4 << endl;
}

class A {
public:
void f() {cout << "A::f(): " << v5_ << " " << v6_ << endl;}
private:
int v5_; // class, non static
static int v6_; // class, static
};
int A::v6_;

int mainRemy() {
cout << "main(): " << v1 << " " << v2 << endl;
f();
A a;
a.f();
return 0;
}

void g()
{
long init[] = { 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF };
}

int main() {
g();
return mainRemy();
}

--
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++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Michael Doubez
On 6 juil, 11:12, Rémi Moyen wrote:
Bonjour,

En relisant un bout de code, je suis tombé sur une classe qui contient
un int comme membre, et bien que cette variable ne soit jamais
initialisée explicitement, elle a toujours la valeur 0 et le code
marche correctement. Ce que je ne comprends pas vraiment, je croyais
que les variables locales n'étaient pas initialisées à 0
automatiquement...


[snip]
Si quelqu'un pouvait m'expliquer, ça m'aiderait pas mal... Merci
d'avance !



As tu essayé avec divers degré d'optimisation ? Si je me souviens
bien, j'ai eu ce cas pour un programme qui marchait bien en mode debug
mais qui plantait en -02 à cause d'une variable non initialisée qui
prenait une valeur aléatoire.

--
Michael
Avatar
Michel Decima
Rémi Moyen a écrit :
On 6 juil, 10:26, Fabien LE LEZ wrote:

[snip l'exemple, merci de confirmer que les 0 dans v3 et A::v5_ ne
sont pas imposés]

Si le standard n'indique pas de valeur par défaut, l'implémentation
est libre d'assigner la valeur qu'elle veut. Ça peut être 0, ou la
suite d'octets qui se trouvait là en mémoire, ou 42.





2) l'OS ou le compilateur mettent toujours systématiquement à 0, c'est
un choix d'implémentation (idem, c'est pas comme ça que ça marche) ;



Je crois que j'ai déjà rencontré un compilateur idiot qui mettait
systématiquement les variables à 0 en mode debug, et ne le faisait
pas en mode release (pour le jeu de parametre par defaut de
debug/release).
Evidemment, ca donne des resultats interessants, un programme qui
fonctionne comme attendu en mode debug peut faire n'importe quoi
une fois optimise...

Sur le compilateur xlC que j'ai sous la main, il y a une option
-qinitauto pour donner une valeur initiale specifique et connue
au variables automatiques :

#include <iostream>
int main() {
int i ;
std::cout << std::hex << i << "n" ;
}

xlC -qinitauto init.cc
"init.cc", line 6.30: 1540-1102 (W) "i" might be used before it is set.

./a.out
ffffffff

Et si on n'utilise pas cette option, on a bien une valeur par defaut,
un peu plus explicite:

xlC -qinitauto init.cc
"init.cc", line 6.30: 1540-1102 (W) "i" might be used before it is set.

./a.out
deadbeaf

la premiere fois, ca surprend ;)
Avatar
Rémi Moyen
On 6 juil, 11:04, Michael Doubez wrote:

As tu essayé avec divers degré d'optimisation ? Si je me souviens
bien, j'ai eu ce cas pour un programme qui marchait bien en mode debug
mais qui plantait en -02 à cause d'une variable non initialisée qui
prenait une valeur aléatoire.



Je n'y avais pas pensé (et pourtant j'ai ajouté un -O1 rien que pour
pouvoir activer -Wuninitialized !). En l'occurrence, ça ne change rien
au résultat.

Tiens, à partir de -O3, j'ai un 2ème warning :

main.cpp: In function `void f()':
main.cpp:12: warning: 'v3' might be used uninitialized in this
function
main.cpp: In function `int main()':
main.cpp:12: warning: 'v3' might be used uninitialized in this
function

Amusant...
--
Rémi Moyen
Avatar
Rémi Moyen
On 6 juil, 10:56, Jean-Marc Bourguet wrote:

> Cependant, j'ai encore un doute avec le warning de g++ : pourquoi il
> ne m'a pas prévenu pour A::v5_ ?

> Est-ce que c'est parce qu'il n'arrive pas à tracer de manière fiabl e
> tout le code et qu'au moment où j'appelle A::f(), il est trop
> difficile pour lui de savoir si la variable a été initialisée ou non,

Vraisemblablement quelque chose du genre.



OK.

> Par curiosité et parce que j'aimerais bien le faire moi-même, tu pe ux
> me donner le programme modifié ?

Voici (remarque l'ajout du dummy[1] qui a ete necessaire, j'ai pas ete vo ir
l'assembleur genere pour comprendre pourquoi).



Merci ! Y'a juste une petite faute de frappe, la fonction devrait
s'appeler mainRemi(), pas mainRemy() (ou mainRémi(), si quelqu'un
arrive à mettre des accents dans un identifiant ?) ;-)
--
Rémi Moyen
Avatar
Fabien LE LEZ
On Mon, 6 Jul 2009 03:39:00 -0700 (PDT), Rémi Moyen
:

si quelqu'un
arrive à mettre des accents dans un identifiant ?



Je crois avoir lu quelque part que la norme l'autorise. Mais je ne
sais pas s'il existe des implémentations de cette mauvaise idée.
1 2 3