OVH Cloud OVH Cloud

surprise (this = 0)

8 réponses
Avatar
ByB
J'ai été plutôt surpris de constater que :

class Toto
{
// SNIP constructeurs et destructeurs supposés corrects

public :
int titi;
void Affiche()
{
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}

void Affiche2()
{
titi = 1;
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}
}

main
{
Toto *t = 0;
t->Affiche(); // cela fonctionne !
t->Affiche2(); // cela "plante"
}

Que l'appel de Affiche2() fasse planter, je comprends, car
l'utilisation de titi est équivalente ) l'utilisation de this->titi, et
ici, this = 0 (si j'ai bien compris).

Mais je suis plutôt surpris que t->Affiche() marche sans problèmes, du
moins avec le compilateur Visual C++ 6.0 de Microsoft ...
Comment cela s'explique t-il ?

PS : Je sais que MessageBox() n'est pas une fonction C++ standard, mais
c'est juste un exemple, tiré d'un programme développé sous Windows ...




--
Il y a un seul cas où il est convenable d'aborder une femme laide.
C'est pour lui demander si elle ne connaît pas l'adresse d'une jolie
femme.
[Pierre Desproges]

8 réponses

Avatar
James Kanze
ByB wrote:
J'ai été plutôt surpris de constater que :


class Toto
{
// SNIP constructeurs et destructeurs supposés corrects


public :
int titi;
void Affiche()
{
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}


void Affiche2()
{
titi = 1;
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}
}


main
{
Toto *t = 0;
t->Affiche(); // cela fonctionne !
t->Affiche2(); // cela "plante"
}


Que l'appel de Affiche2() fasse planter, je comprends, car
l'utilisation de titi est équivalente ) l'utilisation de
this->titi, et ici, this = 0 (si j'ai bien compris).


À peu près. Ce que tu décris, c'est sans doute ce qui se passe
avec ton implémentation -- et j'imagine ça serait pareil avec
beaucoup d'autres implémentations. Mais du point de vue du
langage, c'est un comportement indéfini, et je me suis déjà
servi d'une implémentation où ça n'aurait pas provoqué une
erreur évidente.

Mais je suis plutôt surpris que t->Affiche() marche sans
problèmes, du moins avec le compilateur Visual C++ 6.0 de
Microsoft ... Comment cela s'explique t-il ?


Que c'est un comportement indéfini, et que parfois, ça peut
donner l'impression de fonctionner. Ici, c'est probable que le
code généré ne se sert jamais du pointeur this -- donc, le fait
qu'il ait une valeur loufoque passe inaperçu.

Je me suis aussi servi une fois d'une compilateur où les deux
appels auraient provoqué une erreur. Au point de l'appel, sans
jamais entrer dans la fonction.

PS : Je sais que MessageBox() n'est pas une fonction C++
standard, mais c'est juste un exemple, tiré d'un programme
développé sous Windows ...


La question, c'est est-ce qu'il se sert de quelque chose dans
Toto, pour que le compilateur ait besoin d'utiliser le pointeur
this qu'on a passé à la fonction. Apparamment, non, et donc,
Affiche a l'air de marcher.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

Avatar
Aurelien Regat-Barrel
J'ai été plutôt surpris de constater que :

class Toto
{
// SNIP constructeurs et destructeurs supposés corrects

public :
int titi;
void Affiche()
{
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}

void Affiche2()
{
titi = 1;
MessageBox(0,"COUCOU","COUCOU",MB_OK);
}
}

main
{
Toto *t = 0;
t->Affiche(); // cela fonctionne !
t->Affiche2(); // cela "plante"
}


Tu as essayé:

int i = 0;
Toto *t = reinterpret_cast<Toto*>( &i );
t->Affiche2();
cout << i;

:-)

--
Aurélien Regat-Barrel

Avatar
kanze
Aurelien Regat-Barrel wrote:
J'ai été plutôt surpris de constater que :



[Exemple de comportement indéfini coupé...]

Tu as essayé:

int i = 0;
Toto *t = reinterpret_cast<Toto*>( &i );
t->Affiche2();
cout << i;

:-)


Ce qui doit faire quoi ?

Sur deux de mes machines (Sun Sparc, PC avec AMD 64 Bits), un
pointeur a 8 octets, et un int 4 -- on récupère donc quatre
octets avec des valeurs aléatoire dans t.

Note aussi qu'il y a eu des architectures (plusieurs, même) où
un pointeur null n'avait pas tous les bits à zéro ; où même si
int et char* avait la même taille, t n'aurait pas une valeur de
pointeur null après ta manip.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Aurelien Regat-Barrel

Tu as essayé:



int i = 0;
Toto *t = reinterpret_cast<Toto*>( &i );
t->Affiche2();
cout << i;



:-)



Ce qui doit faire quoi ?

Sur deux de mes machines (Sun Sparc, PC avec AMD 64 Bits), un
pointeur a 8 octets, et un int 4 -- on récupère donc quatre
octets avec des valeurs aléatoire dans t.


Je ne sais pas si tu as noté le &i et non i tout seul => je cherche à ce
que t == &i, donc que t->titi == i ; Toto étant définie ainsi:

class Toto {
public:
int titi;
void Affiche2() { titi = 1; }
};

J'ai testé vite fait (VC++), ça affiche 1.

Note aussi qu'il y a eu des architectures (plusieurs, même) où
un pointeur null n'avait pas tous les bits à zéro ; où même si
int et char* avait la même taille, t n'aurait pas une valeur de
pointeur null après ta manip.


--
Aurélien Regat-Barrel


Avatar
Sylvain
Aurelien Regat-Barrel wrote on 27/04/2006 11:34:

Je ne sais pas si tu as noté le &i et non i tout seul => je cherche à ce
que t == &i, donc que t->titi == i ; Toto étant définie ainsi:



on avait compris que tu espérais en effet mapper la data membre titi sur
la pile de l'appellant:
- cela est très compilo dépendant,
- cela dépend de la convention d'appel,
- cela implique des présupposés sur la classe

au mieux tu mets en évidence un bug qui pourrait pas ne pas détecté du
premier coup.

Sylvain.

Avatar
Aurelien Regat-Barrel
Aurelien Regat-Barrel wrote on 27/04/2006 11:34:


Je ne sais pas si tu as noté le &i et non i tout seul => je cherche à
ce que t == &i, donc que t->titi == i ; Toto étant définie ainsi:



on avait compris que tu espérais en effet mapper la data membre titi sur
la pile de l'appellant:
- cela est très compilo dépendant,
- cela dépend de la convention d'appel,
- cela implique des présupposés sur la classe

au mieux tu mets en évidence un bug qui pourrait pas ne pas détecté du
premier coup.


Oui biensûr c'est pas quelque chose à utiliser, c'était juste un
exemple. Celà dit, pour un cas aussi simple, ça me semble pas si
aléatoire que cela (la convention d'appel est normalisée il me semble,
Toto n'a pas de vtable, ...).
Tu as un exemple en tête ?

--
Aurélien Regat-Barrel


Avatar
Sylvain
Aurelien Regat-Barrel wrote on 27/04/2006 16:07:

Tu as un exemple en tête ?




pas contredisant ton exemple dans sa simplicté (pas de vtable, etc).
je voulais juste souligner que ça peut donner l'impression de marcher
dans un cas très simple mais que cela résulte d'un effet de bord plus
génant (non détection de bug) qu'utile.

Sylvain.


Avatar
kanze
Aurelien Regat-Barrel wrote:

Tu as essayé:

int i = 0;
Toto *t = reinterpret_cast<Toto*>( &i );
t->Affiche2();
cout << i;

:-)


Ce qui doit faire quoi ?

Sur deux de mes machines (Sun Sparc, PC avec AMD 64 Bits),
un pointeur a 8 octets, et un int 4 -- on récupère donc
quatre octets avec des valeurs aléatoire dans t.


Je ne sais pas si tu as noté le &i et non i tout seul => je
cherche à ce que t == &i, donc que t->titi == i ; Toto étant
définie ainsi:

class Toto {
public:
int titi;
void Affiche2() { titi = 1; }
};

J'ai testé vite fait (VC++), ça affiche 1.


OK. J'avais effectivement râté un & quelque part.

Maintenant, ajoute une fonction virtualle et voir ce qui se
passe. Dans la mesure où la classe n'est pas un POD, tu n'as pas
de garantie.

Mais je n'avais pas vu le smiley non plus. Alors, je suppose que
ton propos était plutôt de montre jusqu'où on peut aller, et que
ça donne encore l'impression de marcher. (Si tu veux vraiment
voir jusqu'où on peut aller avec le comportement indéfini qui
marche, il y a un exemple dans Coplien où il change le vptr,
pour qu'une instance d'une classe se comporte comme une autre.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34