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

Comportement indéfini ou pas ?

40 réponses
Avatar
David Côme
Bonjour à tous.
Est ce que ce code à un comportement indéfini ?

//iostream est inclue , ...
int a; //(1)
cout<< a; // (2)

Pour ma part:
Je pense que la valeur de a est indéfinie car (1) créer une variable sur
la pile sans lui affecter de valeur.
Elle prend donc la valeur de ce qui se trouvait avant à cette place.
Il n'y a pas de moyen de connaitre cette valeur. La valeure de a est
indéfini même si certain compilo réalisé une affectation par défaut
(je pense à VC++ en mode débug , on peut confirmer ?)

Par contre la 2eme ligne, elle a un comportement totalement défini.Elle va
afficher la valeur de a, qui peut être n'importe quoi.

Suis-je dans le vrai ?

Merci.

10 réponses

1 2 3 4
Avatar
James Kanze
On Feb 27, 7:10 pm, Fabien LE LEZ wrote:
On Wed, 27 Feb 2008 18:56:42 +0100, David Côme :

int a; //(1)


Jusque-là, pas de problème, même si l'utilité d'un tel code
m'échappe.


On peut vouloir le faire si la valeur d'initialization dépend
d'un bloc de code, avec par exemple des if. Mais ce n'est pas
courant, et même dans ce cas-ci, je préfèrerais l'initialiser
avec quelque chose (peut-être 0), simplement pour que le code
soit déterministe, même dans le cas d'erreur plus tard qui fait
qu'on ne l'initialise pas.

cout<< a; // (2)


C'est de toutes façons un comportement indéfini : dans le
meilleur des cas, ça affichera un entier, sans qu'il soit
possible d'en prévoir la valeur à l'avance.


Dans le meilleur des cas, ça provoquera un core dump. Avec la
plupart des implémentations, en revanche, ça affichera une
valeur numérique non-spécifiée (dans le langage de la norme,
c-à-d même pas garantie d'être la même deux fois de suite).

Il me semble que selon la norme, c'est un comportement
indéfini tout court (On ne peut pas du tout prévoir le
comportement du code) ; toutefois, en pratique, je
m'attendrais à ce qu'un entier quelconque soit effectivement
affiché. Et il y a même de bonnes chances pour que ce soit le
même à chaque exécution, tant qu'on ne recompile pas.


Dans les cas simple, oui. Si par exemple ça, dans le main(),
c'est tout son programme. Dans les cas plus compliqué, je n'y
compterais pas -- si ce bout de code se trouve dans une
fonction, et qu'on a appelé d'autres fonctions avant, ce qu'il
affiche dependrait de ce qu'ont fait ces autres fonctions, d'une
façon assez imprévisible.

--
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
James Kanze
On Feb 27, 8:47 pm, Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On Wed, 27 Feb 2008 18:56:42 +0100, David Côme
:

int a; //(1)


Jusque-là, pas de problème, même si l'utilité d'un tel code
m'échappe.

cout<< a; // (2)


C'est de toutes façons un comportement indéfini : dans le
meilleur des cas, ça affichera un entier, sans qu'il soit
possible d'en prévoir la valeur à l'avance.

Il me semble que selon la norme, c'est un comportement
indéfini tout court (On ne peut pas du tout prévoir le
comportement du code) ;


J'ai pas vérifié, mais c'est ce que je pense. En particulier,
ça peut être une valeur déclanchant une exception (trois cas
plausibles: la représentation de ce qui serait -0 sur une
machine en complément à 1 ou grandeur et signe qui n'admet pas
de -0; mauvais tags sur une machine où les valeurs ont un tag;
il y a eu une machine n'ayant que des nombres en virgule
flottante, mais où certaines opérations trappaient si une
donnée n'était pas entière).


Une autre possibilité (qui existe réelement) : l'implémentation
traque les accès, maintient un bit map de ce qui est initialisé,
et ce qui ne l'est pas, et vérifie à chaque accès. (Purify le
fait, par exemple, et le code ci-dessus génèrerait une sortie
d'erreur de la part de Purify.)

[...]
Et ca dépend aussi du système, tous ne remettent pas toute la
mémoire à 0 entre processus (c'est certainement une mauvaise
idée de ne pas le faire sur des machines à usage général).


À 0, je ne sais pas, mais une machine à usage général est bien
obligé d'y écrire quelque chose, pour des raisons de sécurité.
(La mémoire qui est affectée à mon processus aurait bien pû
appartenir à un processus à toi avant, et en contenir des
données confidentielles.)

--
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
James Kanze
On Feb 28, 2:33 am, Sylvain wrote:
Gabriel Dos Reis wrote on 27/02/2008 23:37:

surtout je ne vois pas ce que serait un *int* "invalide"
!! vous avez des machines sur lesquelles un int, disons
32 bits, peut contenir toutes les valuers entières entre
0x00000000 et 0xFFFFFFFF *plus* d'autres valeurs invalides
???


So ?


so, vous avez des machines sur lesquelles un int, disons 32
bits, peut contenir toutes les valuers entières entre 0x0 et
0xFFFFFFFF *plus* d'autres valeurs, accessoirement invalides
???


C'est tout à fait concevable, et il a même existé des systèmes
où la mémoire a des bits de parité. Invisible au programme, mais
initialisés uniquement à la première écriture.

--
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
James Kanze
On Feb 28, 6:25 am, Jean-Marc Bourguet wrote:
Sylvain writes:

On peut aussi
parfaitement imaginer une implémentation qui vérifierait
dynamiquement la non utilisation de variables non intialisées.


Pas besoin de l'imaginer. Chez la plupart de mes clients, on
interdisait le déployement du code sans qu'il ait tourné sur un
tel système (Purify, en l'occurance). (Je me rappelle le
problème que ça a causé quand on voulait écrire sur disque une
struct avec un char[n] initializé avec strcpy(). Purify râlait
bien qu'on écrivait des octets qui n'avaient pas été
initialisés.)

--
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
Jean-Marc Bourguet
James Kanze writes:

On Feb 28, 6:25 am, Jean-Marc Bourguet wrote:
Sylvain writes:

On peut aussi
parfaitement imaginer une implémentation qui vérifierait
dynamiquement la non utilisation de variables non intialisées.


Pas besoin de l'imaginer. Chez la plupart de mes clients, on
interdisait le déployement du code sans qu'il ait tourné sur un
tel système (Purify, en l'occurance). (Je me rappelle le
problème que ça a causé quand on voulait écrire sur disque une
struct avec un char[n] initializé avec strcpy(). Purify râlait
bien qu'on écrivait des octets qui n'avaient pas été
initialisés.)


Purify fait un peu trop pour qu'on puisse le considerer comme part de
l'implementation (je l'ai deja vu raler sur du code genere par le
compilateur sans que rien dans le source ne pose probleme par exemple),
mais je l'avais en tete quand j'ai ecrit cela.

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
Sylvain
James Kanze wrote on 28/02/2008 09:42:

so, vous avez des machines sur lesquelles un int, disons 32
bits, peut contenir toutes les valuers entières entre 0x0 et
0xFFFFFFFF *plus* d'autres valeurs, accessoirement invalides
???


C'est tout à fait concevable, et il a même existé des systèmes
où la mémoire a des bits de parité. Invisible au programme, mais
initialisés uniquement à la première écriture.


la mémoire avec parité ou encore ECC existe toujours.
ses états sont invisibles du code utilisateur comme de toute librairie
standard, y compris celle de cout.

même dans ce cas je n'arrive pas à imaginer une valeur spéciale faisant
traper l'injecteur.

Sylvain.


Avatar
James Kanze
On Feb 28, 12:07 pm, Sylvain wrote:
James Kanze wrote on 28/02/2008 09:42:

so, vous avez des machines sur lesquelles un int, disons 32
bits, peut contenir toutes les valuers entières entre 0x0 et
0xFFFFFFFF *plus* d'autres valeurs, accessoirement invalides
???


C'est tout à fait concevable, et il a même existé des systèmes
où la mémoire a des bits de parité. Invisible au programme, mais
initialisés uniquement à la première écriture.


la mémoire avec parité ou encore ECC existe toujours. ses
états sont invisibles du code utilisateur comme de toute
librairie standard, y compris celle de cout.


Certes, mais il n'est pas forcement bien initialisé à la mise
sous tension. Alors, si tu lis une valeur à une adresse qui n'a
jamais été écrite, tu risques bien un plantage.

même dans ce cas je n'arrive pas à imaginer une valeur
spéciale faisant traper l'injecteur.


Qu'est-ce qui se passe s'il y a une erreur de parité, alors ?

(Tu peux imaginer ce que tu veux, j'ai déjà travaillé sur de
tels systèmes. En C, en tout cas.)

--
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
Gabriel Dos Reis
James Kanze writes:

| On Feb 28, 6:25 am, Jean-Marc Bourguet wrote:
| > Sylvain writes:
|
| > On peut aussi
| > parfaitement imaginer une implémentation qui vérifierait
| > dynamiquement la non utilisation de variables non intialisées.
|
| Pas besoin de l'imaginer. Chez la plupart de mes clients, on
| interdisait le déployement du code sans qu'il ait tourné sur un
| tel système (Purify, en l'occurance). (Je me rappelle le
| problème que ça a causé quand on voulait écrire sur d isque une
| struct avec un char[n] initializé avec strcpy(). Purify râlait
| bien qu'on écrivait des octets qui n'avaient pas été
| initialisés.)

Ou, le compilateur peut lui meme generer des instructions de trap.

-- Gaby
Avatar
Sylvain
James Kanze wrote on 28/02/2008 15:46:
On Feb 28, 12:07 pm, Sylvain wrote:
James Kanze wrote on 28/02/2008 09:42:

so, vous avez des machines sur lesquelles un int, disons 32
bits, peut contenir toutes les valuers entières entre 0x0 et
0xFFFFFFFF *plus* d'autres valeurs, accessoirement invalides
???


C'est tout à fait concevable, et il a même existé des systèmes
où la mémoire a des bits de parité. Invisible au programme, mais
initialisés uniquement à la première écriture.


la mémoire avec parité ou encore ECC existe toujours. ses
états sont invisibles du code utilisateur comme de toute
librairie standard, y compris celle de cout.


Certes, mais il n'est pas forcement bien initialisé à la mise
sous tension. Alors, si tu lis une valeur à une adresse qui n'a
jamais été écrite, tu risques bien un plantage.


ah bon ?! il y a de la mémoire avec bit de parité (1 bit) ou avec
correction d'erreur (n bits) qui fait exprès de contenir des valeurs
d'erreurs - ou plutôt il existe des cartes dont le controleur mémoire
fait expres de générer n'importe quoi ... je ne savais pas.

j'aurais même penser qu'une telle situation ne pouvait pas durer plus de
quelques nanosecondes (temps de "refresh" moyen), à moins que la mémoire
"qui n'a jamais été écrite" ne soit pas rafraichie...


même dans ce cas je n'arrive pas à imaginer une valeur
spéciale faisant traper l'injecteur.


Qu'est-ce qui se passe s'il y a une erreur de parité, alors ?


en ECC ? le controleur l'a déjà corrigé.
avec bit de parité simple, ça dépends du BIOS, mais cela ne regarde
nullement le code - avec variables non initialisées - chargé; lorsqu'il
est chargé, tous ces octets ont une parité correcte ou le système (le
hard!) était déjà défaillant.

(Tu peux imaginer ce que tu veux, j'ai déjà travaillé sur de
tels systèmes. En C, en tout cas.)


fort bien, "et donc" (c)(r)(tm) ?

Sylvain.




Avatar
James Kanze
On Feb 28, 9:09 pm, Sylvain wrote:
James Kanze wrote on 28/02/2008 15:46:
On Feb 28, 12:07 pm, Sylvain wrote:
James Kanze wrote on 28/02/2008 09:42:

so, vous avez des machines sur lesquelles un int, disons 32
bits, peut contenir toutes les valuers entières entre 0x0 et
0xFFFFFFFF *plus* d'autres valeurs, accessoirement invalides
???


C'est tout à fait concevable, et il a même existé des systèmes
où la mémoire a des bits de parité. Invisible au programme, mais
initialisés uniquement à la première écriture.


la mémoire avec parité ou encore ECC existe toujours. ses
états sont invisibles du code utilisateur comme de toute
librairie standard, y compris celle de cout.


Certes, mais il n'est pas forcement bien initialisé à la mise
sous tension. Alors, si tu lis une valeur à une adresse qui n'a
jamais été écrite, tu risques bien un plantage.


ah bon ?! il y a de la mémoire avec bit de parité (1 bit) ou avec
correction d'erreur (n bits) qui fait exprès de contenir des valeurs
d'erreurs - ou plutôt il existe des cartes dont le controleur mémoire
fait expres de générer n'importe quoi ... je ne savais pas.


À la mise sous tension, la mémoire contient effectivement
n'importe quoi. (Aussi, la plupart des contrôleurs dont je me
suis servi dans le temps prévoyaient bien une commande pour
écrire des valeurs erronées. Pour des motifs de test, si rien
d'autre. Mais le problème réel, c'est que jusqu'à la première
écriture, le contenu n'est pas défini, et il y a des chances
qu'il soit invalid.)

j'aurais même penser qu'une telle situation ne pouvait pas
durer plus de quelques nanosecondes (temps de "refresh"
moyen), à moins que la mémoire "qui n'a jamais été écrite" ne
soit pas rafraichie...


Dans le cas où je l'ai rencontré, il s'agissait des mémoires
statiques, sans refraiche. Mais je conçois bien aussi des cas où
des mémoires dynamiques ne font pas jouer la détection d'erreurs
lors des cycles de refraiche.

même dans ce cas je n'arrive pas à imaginer une valeur
spéciale faisant traper l'injecteur.


Qu'est-ce qui se passe s'il y a une erreur de parité, alors ?


en ECC ? le controleur l'a déjà corrigé.


Il ne peut pas en corriger toutes les erreurs possibles. Que des
erreurs d'un seul bit, et certaines de deux bits.

avec bit de parité simple, ça dépends du BIOS, mais cela ne
regarde nullement le code - avec variables non initialisées -
chargé; lorsqu'il est chargé, tous ces octets ont une parité
correcte ou le système (le hard!) était déjà défaillant.


Tu parles comme si on chargeait la mémoire à partir d'un disque.
(Dans un système classique, évidemment, il n'y a pas de chances
que ça se produisent, parce que le système écrit toute la
mémoire avant de te la donner.) Moi, j'ai rencontré le problème
dans des systèmes embarqués, avec le programme en PROM, et pas
de disque.

--
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





1 2 3 4