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

[newbie] problème débile

64 réponses
Avatar
Azuriel
Est-ce possible de déclarer une propriété de même classe que celle qui
la possède ?

class A
{
A a;
};

ne compile pas.

10 réponses

Avatar
kanze
wrote:
kanze wrote:
Sylvain wrote:
Michel Decima wrote on 28/09/2006 21:50:
Sylvain wrote:

faute de pouvoir ne rien lui fournir, on peut lui
fournir rien en le faisant passer pour qlq chose ...

A* first = null;
A a(*first);


Est ce que *first a un comportement defini si first vaut
NULL ?


quelle importance puisqu'il N'est PAS déréférencé ...


Et c'est quoi, exactement, l'expression *first, si ce n'est pas
la déférencement de first.


*first n'implique pas forcément un déréférencement 'mécanique'
du pointeur first.


Est c'est quoi, l'opérateur * (unaire), si ce n'est pas
l'opérateur de déréferencement ?

En fait, selon la norme actuelle C++ et la norme C90, c'est bien
un déréferencement, et un comportement indéfini. Et je me suis
déjà servi d'un compilateur qui génère un core dump dans de tels
cas.

Le comité C++ est en train de discuter la question, je crois que
la tendance actuelle, c'est que c'est un comportement indéfini
si (et seulement si) il y a une conversion lvalue en rvalue ou
un essai de modifier la valeur référencée. Mais ce n'est pas
encore la norme définitive. (Et je me démande s'ils iront
jusqu'à permettre une référence nulle. Ça a des implications non
negligeables.)

int *p = 0;

int &r = *p; // pas de déréférencement, seule l'adresse est
//utilisée => ok


Selon la norme C++ aujourd'hui, ce n'est pas OK, c'est un
comportement indéfini. Un compilateur peut générer code qui le
vérifie -- je me suis déjà servi d'un compilateur qui le
faisait.

int v = *p; // déréférencement => crash

La seconde ligne DOIT passer sur n'importe quel compilateur
(warning au maximum).


Dans la pratique, parce qu'il n'y a comportement indéfini que si
la ligne s'exécute réelement. Si le compilateur peut déterminer
que la ligne s'exécute, pour toutes les entrées possibles, il
peut réfuser de compiler le programme. Dans la pratique, les cas
où il peut le déterminer sont assez peu pour qu'il ne fait pas
l'effort de le faire.

Si vous connaissez un compilateur pour lequel cette ligne ne
passe pas, je serais intéressé d'en connaitre le nom.


Comme j'ai dit, je me suis déjà servi d'un compilateur (un
ancien Green Hills, environ 1992/1993) qui générait du code pour
tester si le pointeur était nul, à chaque déréférence. C-à-d à
chaque utilisation de l'opérateur unaire *. Une instruction du
genre :
int& r = *p ;
où p était un pointeur nul, provoquait un core dump.

--
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
Michel Decima
Michel Decima wrote on 17/10/2006 00:14:

Je connais au moins un compilateur qui refuse ce code. Si vous pensez
que cela n'est pas conforme, je vous laisse l'honneur du bug report.

Comeau C/C++ 4.3.8 (Aug 19 2006 13:36:48) for ONLINE_EVALUATION_Alpha1


un bug report sur une version d'éval ?!??


Le mot ONLINE_EVALUATION porte sur l'acces cgi au compilateur. On
peut tester les version beta et les version stables.

c'est payé combien par report ?


Aussi cher qu'un post sur fclc++ ;)


Avatar
kanze
wrote:
Michel Decima wrote:

*first n'implique pas forcément un déréférencement 'mécaniq ue' du
pointeur first.

int *p = 0;

int &r = *p; // pas de déréférencement, seule l'adresse est
//utilisée => ok

int v = *p; // déréférencement => crash

La seconde ligne DOIT passer sur n'importe quel compilateur (warning au
maximum).


Qu'est ce qui JUSTIFIE cette affirmation dans la norme ?


L'opération d'initialisation d'une référence.
int &r = x;

2 cas :
- "x" est une lvalue ("x" doit être du type de la référence) :
l'opération d'initialisation prend l'adresse de "x" => "x" n'est pas
évalué.


D'où est-ce que tu tiens ça ? Je ne le trouve pas dans ma copie
de la norme (ni la version C++98, ni la version de travail la
plus récente). Il s'agit ici de l'initialilsation d'une
référence, et la version de travail dit (§8.5.3) : « A
variable declared to be a T&, that is reference to type T
(8.3.2), shall be initialized by an object, or function, of type
T or by an object that can be converted into a T. » Note bien
qu'il exige un *objet* de type T.

- "x" est une rvalue ("x" ne doit pas forcément être du type de la
référence): Conversion implicite de type + création d'une variable
temporaire. => "x" est évalué. A partir de là, l'initialisation
possède un objet avec une adresse. Par conséquent, retour au premier
cas.


C'est un cas à part, qui n'est légal que si la référence est
const et non volatile, et qui donc ne nous intéresse pas ici.

De plus, un comportement non défini devrait (mais ce n'est pas
toujours le cas...) générer de la part du compilateur
uniquement un warning.


De la définition de « undefined behavior » de la norme
(§1.3.13) : « behavior, such as might arise upon use of an
erroneous program construct or erroneous data, for which this
International Standard imposes NO requirements. [...]
Note: permissible undefined behavior ranges from ignoring the
situation completely with unpredictable results, to behaving
during translation or program execution in a documented manner
characteristic of the environment (with or without the issuance
of a diagnostic message), to TERMINATING A TRANSLATION or
execution (with the issuance of a diagnostic message). »
(La dernière phrase fait partie d'une note, et a donc plutôt un
caractère explicatif, et non normatif. Mais elle indique bien
l'intention.)

En fait, le comportement indéfini dans ce cas-ci résulte de
l'exécution de l'instruction. Et il y a un argument
traditionnel, datant déjà depuis les débuts de la norme C, que
dans de tels cas, le compilateur n'a droit à terminer la
traduction que s'il peut prouver que l'instruction serait
exécutée. Donc :

int main() {
int* p = 0 ;
int& r = *p ;
}

donne droit à une erreur de compilation, sans génération du
fichier d'objet, tandis que :

int main( int argc, char** argv ) {
if ( argc > 1 ) {
int* p = 0 ;
int& r = *p ;
}
}

non. Du point de vue de la qualité de l'implémentation, et
quelle que soit l'interprétation « correcte » de la norme ici,
je trouve qu'un compilateur qui réfuse de compiler ce deuxième
programme est bien.

--
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
kanze
wrote:
Michel Decima wrote:

L'opération d'initialisation d'une référence.
int &r = x;

2 cas :
- "x" est une lvalue ("x" doit être du type de la référence) :
l'opération d'initialisation prend l'adresse de "x" => "x" n'est pas
évalué.
- "x" est une rvalue ("x" ne doit pas forcément être du type de la
référence): Conversion implicite de type + création d'une varia ble
temporaire. => "x" est évalué. A partir de là, l'initialisati on
possède un objet avec une adresse. Par conséquent, retour au prem ier
cas.


Si je considere

int& r = *(int*)0;

dans lequel des 2 cas on se place ? Quel devrait etre le
resultat de la compilation ?


*(int*) 0
Cette expression spécifie un objet de type int dont l'adresse
est 0. (<=> définition d'une lvalue)


Non. C'est un cas particulier. Ici, l'expression « 0 » est une
expression constante de type entier qui s'évalue à 0. C'est donc
un constante de pointeur nul. Le convertir en pointeur, donc,
donne un pointeur nul, qui par définition ne désigne aucun objet
(« distinguishable from every other value of pointer to object
or pointer to function type »). S'il peut exister un objet à
l'adresse 0 (rarement le cas sur des machines modernes), le
résultat de ton expression ne peut pas être l'adresse 0. (Et il
a bel et bien existé des machines où l'expression (int*)0
donnait un pointeur dont tous les bits n'étaient pas à zéro, les
anciens Prime, par exemple.)

Et par définition, donc, (int*)0 ne désigne pas un objet, et
*(int*)0 n'est pas légal (parce que l'opérateur * convertit un
pointeur en lvalue, et un lvalue doit désigner un objet).

Définition d'une lvalue dans la norme c++ :
lvalue : une expression qui réfère à un objet.


Tout à fait. Et à quel objet réfère *(int*)0, étant donné que
par définition, (int*)0 est « distinguishable from every other
value of pointer to object or pointer to function type » ?

A un autre endroit, la norme utilise également cette définition
(équivalente dans la pratique):
lvalue : un objet dont on peut prendre l'adresse.


Tout à fait. Et si on prend l'adresse d'un objet, cette adresse
ne doit pas comparer égal à un pointeur nul.

[Précision à ce sujet : la norme c++ indique clairement qu'une
lvalue n'est pas forcément un objet qui peut se placer à
gauche de l'opérateur d'assignement, tel qu'on l'entend
souvent.]

L'expression est donc une lvalue.


Le résultat de l'opérateur unaire * est un lvalue. Par
définition. Mais évidemment, seulement quand l'expression a un
comportement défini -- si le comportement est indéfini, on ne
peut rien dire. (C'est la définition de comportement indéfini
dans la norme.)

(Le fait que le pointeur opérand de l'opérateur * doit désigner
un objet n'est pas aussi clair qu'on aimerait dans la norme
C++ actuelle. Mais la norme C90, sur laquelle elle se base, ne
laisse pas l'ombre d'une doute. Et donné que le résultat est un
lvalue, et, comme tu dis, un lvalue doit désigner un objet, et
que la norme ne permet pas d'objet à l'adresse d'un pointeur
nul, on peut resonner indirectement qu'on a un comportement
indéfini aussi.)

Note que la norme C99 contient un langage spécial pour ce
cas-ci : « [concernant l'opérateur &] If the operand is the
result of a unary * operator, neither that operator nor the &
operator is evaluated and the result is as if both were omitted,
except that the constraints on the operators still apply and the
result is not an lvalue. » Ce texte ne fait pas partie de la
norme C++ actuelle, ni directement, ni par référence. Le comité
C++ est actuellement en train de discuter le cas, et je crois
que la tendance est d'admettre que quelque chose comme &*(int*)0
serait légal. Mais ça ne changerait rien en ce qui concerne
l'initialisation d'une référence, parce que l'initialisation
d'une référence exige un objet, et même légale, une expression
du genre « *(int*)0 » ne désignera jamais un objet. (Sinon,
tout le concepte des pointeurs nuls tombe à l'eau).

J'ajouterai en passant que la motivation du langage spécial dans
C99, c'était des cas comme :

int a[ 5 ] ;
int* end = &a[ 5 ] ;

En C90 (et en C++), c'est un comportement indéfini, parce que
par définition, « a[ 5 ] » et la même chose que
« *(a + 5) », une expression qui déréférence a[5], et que le
déréférencement d'a[5] a un comportement indéfini. Et qu'il
n'est pas question de faire marcher l'équivalent en C++ :

std::vector< int > a( 5 ) ;
int* end = &a[ 5 ] ;

qui provoque un core dump avec g++ ou VC++.

Nous sommes dans le 1er cas.


Où est l'objet ? La norme dit qu'il n'y a pas d'objet où pointe
un pointeur nul.

Le compilateur devrait laisser passer cette ligne (il ne
devrait pas tenter un déréférencement).


Le déréférencement, il y est. C'est la définition de
l'opérateur *.

--
James Kanze (GABI Software) email:
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
kanze
wrote:
Falk Tannhäuser wrote:
schrieb:



[...]
Notamment :
the only way to create such a reference would be to bind it
to the "object" obtained by dereferencing a null pointer


Ce déréférencement "syntaxique" n'implique pas de
déréférencement "mécanique" (c'est-à-dire l'accès à l'objet
référencé).


Ça veut dire quoi, déréférencement « mécanique » ?

Si tu parles de la génération d'une instruction load ou store à
l'adresse, tu as probablement raison. Bien que dans la mesure où
on a un comportement indéfini, la norme n'impose rien au
compilateur.

La question n'est pas s'il y a une déréférencement
« mécanique » (qui n'a de toute façon aucun sens). La question
est si l'instruction marche. Et on t'a répondu, citations à la
main, que la norme dit que c'est un comportement indéfini, et
que donc, on n'a pas la moindre garantie. Et moi, je peux te
dire que je me suis déjà servi d'un compilateur où il ne
marchait pas -- le compilateur généré systèmatiquement des
tests pour un pointeur null chaque fois qu'on se servait de
l'opérateur * unaire.

Je réitère donc ma demande : si vous connaissez un compilateur
qui produit un déréférencement "mécanique" lors de l'opération
d'initialisation de la référence, merci de m'en donner le nom,
je n'en ai encore rencontré aucun, même en se basant une
version debug du code.


Et je te répète, l'histoire d'un déréférencement « mécanique »
ne signifie rien. La question n'est pas tellement : est-ce que
ça marche (par hazard) avec tes compilateurs actuels ?, mais
plutôt, est-ce que c'est garanti de marcher, est-ce que je peux
y compter avec la prochaine version du compilateur, par
exemple ? Et là, je ne connais pas de compilateur qui donne ce
garantie.

N'oublie pas que la tendance est à ajouter des vérifications.
Donc, du code comme :
std::vector< int > a( 5 ) ;
int* end( &a[ 5 ] ) ;
« marchait » avec g++ 3.4 ou VC++ 6, mais ne marche pas avec
g++ 4.1 ni avec VC++ 8. Et même si le code du genre :
int* p = 0 ;
int& r = *p ;
marche avec les compilateurs actuellement, est-ce que tu peux
être sûr qu'il marche avec la prochaine version du compilateur.
La norme ne le garantit pas. Si le vendeur du compilateur en
fait une garantie, c'est bien ; tu peux y compter. Sinon ?
(G++ ne fait pas cette garantie. En ce qui concerne VC++, je ne
sais pas, mais je le doute un peu.)

--
James Kanze (GABI Software) email:
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
Manuel Zaccaria
a écrit:
kanze wrote:
Sylvain wrote:
Michel Decima wrote on 28/09/2006 21:50:
Sylvain wrote:

faute de pouvoir ne rien lui fournir, on peut lui fournir rien en
le
faisant passer pour qlq chose ...

A* first = null;
A a(*first);


Est ce que *first a un comportement defini si first vaut NULL ?


quelle importance puisqu'il N'est PAS déréférencé ...




Bien sur que si... petit rappel:


class A
{
A& a;
A(A another) : a(another) { ... }

/*virtual*/ void execute() { ... }
// virtual ou pas, ça ne change rien au problème

void crash()
{
a.execute();
}
};


// ...

A* zero = 0;
A bizzare(*first);

bizzare.crash();


Ceci dit, je ne vois aucun intérêt à initialiser une référence
avec un pointeur nul. Comme il a déjà été dit et répété, c'est
un comportement indéfini. J'ajoute que ce serait contre-productif
(une pessimisation). Il devient tout-à-coup nécessaire de tester
dans les fonctions membres l'adresse de la référence à coup de :

if(&a != 0) ...

ou pire :

if(this != 0) ...


AMHA, le but des références est justement de GARANTIR que l'objet
existe (this est toujours != 0).

Sinon on utilise un pointeur, pas une référence. Faut pas mélanger
des concepts qui visent des buts différents.

Quand on se bat contre le compilateur, on est toujours perdant!



Et c'est quoi, exactement, l'expression *first, si ce n'est pas
la déférencement de first.



*first n'implique pas forcément un déréférencement 'mécanique' du
pointeur first.

int *p = 0;

int &r = *p; // pas de déréférencement, seule l'adresse est
//utilisée => ok


Mais quel est l'intérêt de faire ça ? Je n'en vois aucun.
Que des ennuis.


int v = *p; // déréférencement => crash



int v = r; // crash aussi

et faire:

if(&r != 0)
{
// blah
}

Ne résoud rien. Pourquoi ne pas tester p et utiliser *p directement ?

La seconde ligne DOIT passer sur n'importe quel compilateur (warning au
maximum). Si vous connaissez un compilateur pour lequel cette ligne ne
passe pas, je serais intéressé d'en connaitre le nom.


James a cité un/des compilateur/s où c'est le cas.


Mes 2 centimes suisses,
Manuel Zaccaria





Avatar
dieu.tout.puissant
kanze wrote:
wrote:
Michel Decima wrote:

L'opération d'initialisation d'une référence.
int &r = x;

2 cas :
- "x" est une lvalue ("x" doit être du type de la référence) :
l'opération d'initialisation prend l'adresse de "x" => "x" n'es t pas
évalué.
- "x" est une rvalue ("x" ne doit pas forcément être du type de la
référence): Conversion implicite de type + création d'une var iable
temporaire. => "x" est évalué. A partir de là, l'initialisa tion
possède un objet avec une adresse. Par conséquent, retour au pr emier
cas.


Si je considere

int& r = *(int*)0;

dans lequel des 2 cas on se place ? Quel devrait etre le
resultat de la compilation ?


*(int*) 0
Cette expression spécifie un objet de type int dont l'adresse
est 0. (<=> définition d'une lvalue)


Non. C'est un cas particulier. Ici, l'expression « 0 » est une
expression constante de type entier qui s'évalue à 0. C'est donc
un constante de pointeur nul. Le convertir en pointeur, donc,
donne un pointeur nul, qui par définition ne désigne aucun objet
(« distinguishable from every other value of pointer to object
or pointer to function type »). S'il peut exister un objet à
l'adresse 0 (rarement le cas sur des machines modernes), le
résultat de ton expression ne peut pas être l'adresse 0. (Et il
a bel et bien existé des machines où l'expression (int*)0
donnait un pointeur dont tous les bits n'étaient pas à zéro, les
anciens Prime, par exemple.)

Et par définition, donc, (int*)0 ne désigne pas un objet, et
*(int*)0 n'est pas légal (parce que l'opérateur * convertit un
pointeur en lvalue, et un lvalue doit désigner un objet).


Oui effectivement, j'ai eu du mal à définir *(int*)0.
J'ai utilisé l'expression "spécifier un objet" (et non définir,
déclarer, ou désigner un objet) qui est, certes, peu probante (voire
fausse), car :

-l'operateur unaire * donne une lvalue.
-une lvalue réfère à un objet.
-(int*)0 n'est pas un objet.

Je vois maintenant que la norme traite ce cas à part, sinon il y a
contradiction sémantique.




Avatar
dieu.tout.puissant
kanze wrote:
wrote:
Falk Tannhäuser wrote:
schrieb:



[...]
Notamment :
the only way to create such a reference would be to bind it
to the "object" obtained by dereferencing a null pointer


Ce déréférencement "syntaxique" n'implique pas de
déréférencement "mécanique" (c'est-à-dire l'accès à l'obj et
référencé).


Ça veut dire quoi, déréférencement « mécanique » ?


Mon explication du terme déréférencement "mécanique" laisse à
désirer. Pour l'améliorer, je vais donc lister le code asm, non
optimisé, généré par mon compilateur, vc++ 8 (pas besoin de bien
connaitre l'assembleur pour comprendre).

int *p = 0;
mov dword ptr [p],0

int &r = *p;
mov eax,dword ptr [p]
mov dword ptr [r],eax

int v = *p;
mov eax,dword ptr [p]
mov ecx,dword ptr [eax] ; déréférencement mécanique
mov dword ptr [v],ecx

Voilà la différence entre l'initialisation d'une référence et d'un
objet.
L'opération d'initialisation de référence n'implique pas de
déréférencement mécanique.

La norme ne tient pas compte de la réalité du terrain (on dirait un
discours politique).

int *p = 0;
int &r = *p;

Il n'y a aucun intérêt à déréférencer (mécaniquement) p. A qu oi
servirait la valeur de l'objet pointé par p? S'il y a
déréférencement dans ce cas-là, il y aura crash à l'exécution. Je
n'ai encore rencontré aucun compilateur qui sacrifie la rapidité du
code (génère une instruction de plus) dans le but de provoquer un
crash.


Et moi, je peux te
dire que je me suis déjà servi d'un compilateur où il ne
marchait pas -- le compilateur généré systèmatiquement des
tests pour un pointeur null chaque fois qu'on se servait de
l'opérateur * unaire.


Si tu peux te souvenir du nom de ce compilateur, j'aimerais vraiment
étudier la manière dont il génère ce genre de test. En effet, pour
que ce test soit valide pour n'importe quelles applications, le
pointeur doit se trouver sur la pile. Sinon (le pointeur est lui-même
un objet pointé se trouvant sur le tas), l'ensemble de cette
opération(test + déréférencement) est non atomique (pas de test&set
pour les déréférencements) et le compilateur doit normalement poser
un lock, sauf si le compilateur ne génère que du code monothreadé.



Avatar
kanze
wrote:
kanze wrote:
wrote:
Falk Tannhäuser wrote:
schrieb:



[...]
Notamment :
the only way to create such a reference would be to bind it
to the "object" obtained by dereferencing a null pointer


Ce déréférencement "syntaxique" n'implique pas de
déréférencement "mécanique" (c'est-à-dire l'accès à l'o bjet
référencé).


Ça veut dire quoi, déréférencement « mécanique » ?


Mon explication du terme déréférencement "mécanique" laisse à
désirer. Pour l'améliorer, je vais donc lister le code asm,


Quel rapport entre le code assembleur, et ce qui est garanti ou
non ?

non optimisé, généré par mon compilateur, vc++ 8 (pas besoin
de bien connaitre l'assembleur pour comprendre).

int *p = 0;
mov dword ptr [p],0

int &r = *p;
mov eax,dword ptr [p]
mov dword ptr [r],eax

int v = *p;
mov eax,dword ptr [p]
mov ecx,dword ptr [eax] ; déréférencement mécanique
mov dword ptr [v],ecx

Voilà la différence entre l'initialisation d'une référence et
d'un objet. L'opération d'initialisation de référence
n'implique pas de déréférencement mécanique.


L'assembleur que génère le compilateur ne nous dit rien sur ce
qui est « impliqué » ou non par le langage. Changer les
options de compilation, changer la contexte où l'instruction
apparaît, changer prèsque n'importe quoi, et le compilateur
génère autre chose.

La norme ne tient pas compte de la réalité du terrain (on
dirait un discours politique).


Elle y tient plus compte que tu n'imagines. En revanche, elle
considère un terrain bien plus vaste que tu n'as l'air
d'imaginer.

int *p = 0;
int &r = *p;

Il n'y a aucun intérêt à déréférencer (mécaniquement) p. A
quoi servirait la valeur de l'objet pointé par p?


Qu'importe ? Il n'y a que toi qui parles d'un déréférencement
potentiel. Après *p, le compilateur a le droit de considérer que
p ne peut pas être nul. Ce qui peut bien modifier la façon qu'il
génère du code.

S'il y a déréférencement dans ce cas-là, il y aura crash à
l'exécution. Je n'ai encore rencontré aucun compilateur qui
sacrifie la rapidité du code (génère une instruction de plus)
dans le but de provoquer un crash.


Il te manque de l'expérience, alors, parce que la plupart des
compilateurs aujourd'hui ont des options pour insérer du code de
vérification dans certains cas. Et ce n'est pas une nouveauté.

Et moi, je peux te dire que je me suis déjà servi d'un
compilateur où il ne marchait pas -- le compilateur généré
systèmatiquement des tests pour un pointeur null chaque fois
qu'on se servait de l'opérateur * unaire.


Si tu peux te souvenir du nom de ce compilateur, j'aimerais
vraiment étudier la manière dont il génère ce genre de test.


Le compilateur, c'était le compilateur de Green Hills. Je ne
sais pas ce qui lui en est devenu aujourd'hui.

Il y a un autre compilateur dont on me dit qu'il faisait pareil.
J'ai un trou de mémoire en ce qui concerne son nom à l'instant,
mais à l'époque, il était assez connu pour tester tout. (Il se
vendait comme compilateur de « contrôle » ; son but n'était
pas de compiler l'exécutable qu'on livrait, mais de compiler
avec un maximum de vérifications, de façon à ce qu'on détecte un
maximum d'erreurs.)

En effet, pour que ce test soit valide pour n'importe quelles
applications, le pointeur doit se trouver sur la pile. Sinon
(le pointeur est lui-même un objet pointé se trouvant sur le
tas), l'ensemble de cette opération(test + déréférencement)
est non atomique (pas de test&set pour les déréférencements)
et le compilateur doit normalement poser un lock, sauf si le
compilateur ne génère que du code monothreadé.


Tu te moques de nous ou quoi ? Si un autre thread peut modifier
le pointeur, l'utilisateur a déjà un lock. Sinon, il a un
comportement indéfini, qui risque réelement de lui causer des
ennuis. (N'oublie pas que la lecture d'un pointeur n'est pas
forcément atomique. Au moins, pas sur un Intel.)

--
James Kanze (GABI Software) email:
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
dieu.tout.puissant

wrote:

S'il y a déréférencement dans ce cas-là, il y aura crash à
l'exécution. Je n'ai encore rencontré aucun compilateur qui
sacrifie la rapidité du code (génère une instruction de plus)
dans le but de provoquer un crash.


Il te manque de l'expérience, alors, parce que la plupart des
compilateurs aujourd'hui ont des options pour insérer du code de
vérification dans certains cas. Et ce n'est pas une nouveauté.


Vérification, oui. Crash intentionnel, non.



Et moi, je peux te dire que je me suis déjà servi d'un
compilateur où il ne marchait pas -- le compilateur généré
systèmatiquement des tests pour un pointeur null chaque fois
qu'on se servait de l'opérateur * unaire.


Si tu peux te souvenir du nom de ce compilateur, j'aimerais
vraiment étudier la manière dont il génère ce genre de test.


Le compilateur, c'était le compilateur de Green Hills. Je ne
sais pas ce qui lui en est devenu aujourd'hui.


Merci. Je vais effecteur quelques recherches sur ce compilateur.


Il y a un autre compilateur dont on me dit qu'il faisait pareil.
J'ai un trou de mémoire en ce qui concerne son nom à l'instant,
mais à l'époque, il était assez connu pour tester tout. (Il se
vendait comme compilateur de « contrôle » ; son but n'était
pas de compiler l'exécutable qu'on livrait, mais de compiler
avec un maximum de vérifications, de façon à ce qu'on détecte un
maximum d'erreurs.)

En effet, pour que ce test soit valide pour n'importe quelles
applications, le pointeur doit se trouver sur la pile. Sinon
(le pointeur est lui-même un objet pointé se trouvant sur le
tas), l'ensemble de cette opération(test + déréférencement)
est non atomique (pas de test&set pour les déréférencements)
et le compilateur doit normalement poser un lock, sauf si le
compilateur ne génère que du code monothreadé.


Tu te moques de nous ou quoi ? Si un autre thread peut modifier
le pointeur, l'utilisateur a déjà un lock. Sinon, il a un
comportement indéfini, qui risque réelement de lui causer des
ennuis. (N'oublie pas que la lecture d'un pointeur n'est pas
forcément atomique. Au moins, pas sur un Intel.)



L'utilisateur a déjà un lock ? Qui l'y oblige ? Heureusement que ce
n'est pas obligatoire notamment dans le domaine de développement de
certains drivers et applications real-time. Par contre le compilateur
se doit d'avoir un comportement consistant : le compilateur doit mettre
un lock, même s'il est peu probable qu'une application s'amuse à
changer un pointeur NULL en un pointeur valide dans une autre thread
(par contre changer un pointeur valide en un autre pointeur valide sans
lock est un cas pratique).